From 3b4814aa2d3dec621dadb52f058ba95a3dc3a86a Mon Sep 17 00:00:00 2001 From: "kumaran.m" Date: Fri, 1 May 2020 19:48:54 +0530 Subject: [PATCH] Initial Commit - NG UI * Roboto and font-awesome fonts are added in package.json * Replace Nginx alpine varient to stable * Devops files are added * Docker file aligned as per community reviews * Enhancement - NS primitive, Azure inclusion and domain name * RWD changes Change-Id: If543efbf127964cbd8f4be4c5a67260c91407fd9 Signed-off-by: kumaran.m --- .editorconfig | 30 + .gitignore | 61 + Dockerfile | 16 +- README.md | 115 +- angular.json | 145 +++ browserslist | 28 + build-debpkg.sh | 6 +- docker/Dockerfile | 54 + nginx/nginx.conf | 34 + package.json | 91 ++ proxy.conf.json | 7 + src/app/AppComponent.html | 19 + src/app/AppComponent.scss | 17 + src/app/AppComponent.ts | 123 ++ src/app/AppModule.ts | 231 ++++ src/app/approutes.module.ts | 114 ++ src/app/dashboard/DashboardComponent.html | 167 +++ src/app/dashboard/DashboardComponent.scss | 160 +++ src/app/dashboard/DashboardComponent.ts | 402 ++++++ src/app/dashboard/DashboardModule.ts | 61 + src/app/instances/InstancesComponent.html | 18 + src/app/instances/InstancesComponent.scss | 17 + src/app/instances/InstancesComponent.ts | 55 + src/app/instances/InstancesModule.ts | 132 ++ .../NetsliceInstancesComponent.html | 44 + .../NetsliceInstancesComponent.scss | 17 + .../NetsliceInstancesComponent.ts | 283 +++++ .../HistoryOperationsComponent.html | 36 + .../HistoryOperationsComponent.scss | 17 + .../HistoryOperationsComponent.ts | 265 ++++ .../ns-instances/NSInstancesComponent.html | 43 + .../ns-instances/NSInstancesComponent.scss | 17 + .../ns-instances/NSInstancesComponent.ts | 278 +++++ .../ns-primitive/NSPrimitiveComponent.html | 92 ++ .../ns-primitive/NSPrimitiveComponent.scss | 34 + .../ns-primitive/NSPrimitiveComponent.ts | 271 ++++ .../ns-topology/NSTopologyComponent.html | 173 +++ .../ns-topology/NSTopologyComponent.scss | 17 + .../ns-topology/NSTopologyComponent.ts | 573 +++++++++ .../pdu-instances/PDUInstancesComponent.html | 36 + .../pdu-instances/PDUInstancesComponent.scss | 17 + .../pdu-instances/PDUInstancesComponent.ts | 197 +++ .../AddPDUInstancesComponent.html | 95 ++ .../AddPDUInstancesComponent.scss | 34 + .../AddPDUInstancesComponent.ts | 197 +++ .../vnf-instances/VNFInstancesComponent.html | 29 + .../vnf-instances/VNFInstancesComponent.scss | 17 + .../vnf-instances/VNFInstancesComponent.ts | 187 +++ .../vnf-link/VNFLinkComponent.html | 18 + .../vnf-link/VNFLinkComponent.scss | 17 + .../vnf-link/VNFLinkComponent.ts | 46 + src/app/k8s/K8sComponent.html | 18 + src/app/k8s/K8sComponent.scss | 17 + src/app/k8s/K8sComponent.ts | 56 + src/app/k8s/K8sModule.ts | 106 ++ .../k8s/k8s-action/K8sActionComponent.html | 27 + .../k8s/k8s-action/K8sActionComponent.scss | 17 + src/app/k8s/k8s-action/K8sActionComponent.ts | 103 ++ .../K8sAddClusterComponent.html | 92 ++ .../K8sAddClusterComponent.scss | 17 + .../k8s-add-cluster/K8sAddClusterComponent.ts | 233 ++++ .../k8s/k8s-add-repo/K8sAddRepoComponent.html | 68 + .../k8s/k8s-add-repo/K8sAddRepoComponent.scss | 17 + .../k8s/k8s-add-repo/K8sAddRepoComponent.ts | 134 ++ .../k8s/k8scluster/K8sClusterComponent.html | 42 + .../k8s/k8scluster/K8sClusterComponent.scss | 17 + src/app/k8s/k8scluster/K8sClusterComponent.ts | 236 ++++ .../k8srepository/K8sRepositoryComponent.html | 35 + .../k8srepository/K8sRepositoryComponent.scss | 17 + .../k8srepository/K8sRepositoryComponent.ts | 198 +++ src/app/layouts/LayoutComponent.html | 37 + src/app/layouts/LayoutComponent.scss | 36 + src/app/layouts/LayoutComponent.ts | 44 + .../breadcrumb/BreadcrumbComponent.html | 27 + .../breadcrumb/BreadcrumbComponent.scss | 81 ++ .../layouts/breadcrumb/BreadcrumbComponent.ts | 140 +++ src/app/layouts/header/HeaderComponent.html | 71 ++ src/app/layouts/header/HeaderComponent.scss | 51 + src/app/layouts/header/HeaderComponent.ts | 106 ++ src/app/layouts/sidebar/SidebarComponent.html | 52 + src/app/layouts/sidebar/SidebarComponent.scss | 225 ++++ src/app/layouts/sidebar/SidebarComponent.ts | 148 +++ src/app/login/LoginComponent.html | 57 + src/app/login/LoginComponent.scss | 156 +++ src/app/login/LoginComponent.ts | 131 ++ src/app/packages/PackagesComponent.html | 18 + src/app/packages/PackagesComponent.scss | 17 + src/app/packages/PackagesComponent.ts | 55 + src/app/packages/PackagesModule.ts | 127 ++ .../InstantiateNetSliceTemplateComponent.html | 98 ++ .../InstantiateNetSliceTemplateComponent.scss | 17 + .../InstantiateNetSliceTemplateComponent.ts | 282 +++++ .../InstantiateNsComponent.html | 103 ++ .../InstantiateNsComponent.scss | 17 + .../instantiate-ns/InstantiateNsComponent.ts | 268 ++++ .../NetsliceTemplateComponent.html | 38 + .../NetsliceTemplateComponent.scss | 20 + .../NetsliceTemplateComponent.ts | 250 ++++ .../ns-packages/NSPackagesComponent.html | 44 + .../ns-packages/NSPackagesComponent.scss | 17 + .../ns-packages/NSPackagesComponent.ts | 258 ++++ .../ns-composer/NSComposerComponent.html | 264 ++++ .../ns-composer/NSComposerComponent.scss | 17 + .../ns-composer/NSComposerComponent.ts | 1089 +++++++++++++++++ .../vnf-composer/VNFComposerComponent.html | 312 +++++ .../vnf-composer/VNFComposerComponent.scss | 17 + .../vnf-composer/VNFComposerComponent.ts | 1020 +++++++++++++++ .../show-content/ShowContentComponent.html | 44 + .../show-content/ShowContentComponent.scss | 17 + .../show-content/ShowContentComponent.ts | 82 ++ .../vnf-packages/VNFPackagesComponent.html | 44 + .../vnf-packages/VNFPackagesComponent.scss | 17 + .../vnf-packages/VNFPackagesComponent.ts | 275 +++++ .../page-not-found/PageNotFoundComponent.html | 27 + .../page-not-found/PageNotFoundComponent.scss | 29 + .../page-not-found/PageNotFoundComponent.ts | 42 + src/app/projects/ProjectsComponent.html | 35 + src/app/projects/ProjectsComponent.scss | 17 + src/app/projects/ProjectsComponent.ts | 203 +++ src/app/projects/ProjectsModule.ts | 86 ++ .../ProjectCreateUpdateComponent.html | 51 + .../ProjectCreateUpdateComponent.scss | 17 + .../ProjectCreateUpdateComponent.ts | 233 ++++ src/app/roles/RolesComponent.html | 18 + src/app/roles/RolesComponent.scss | 18 + src/app/roles/RolesComponent.ts | 58 + src/app/roles/RolesModule.ts | 87 ++ .../RolesCreateEditComponent.html | 92 ++ .../RolesCreateEditComponent.scss | 51 + .../RolesCreateEditComponent.ts | 335 +++++ .../roles-details/RolesDetailsComponent.html | 36 + .../roles-details/RolesDetailsComponent.scss | 17 + .../roles-details/RolesDetailsComponent.ts | 191 +++ .../SDNControllerComponent.html | 18 + .../SDNControllerComponent.scss | 17 + .../sdn-controller/SDNControllerComponent.ts | 55 + src/app/sdn-controller/SDNControllerModule.ts | 82 ++ .../NewSDNControllerComponent.html | 104 ++ .../NewSDNControllerComponent.scss | 17 + .../NewSDNControllerComponent.ts | 151 +++ .../SDNControllerDetailsComponent.html | 43 + .../SDNControllerDetailsComponent.scss | 17 + .../SDNControllerDetailsComponent.ts | 249 ++++ .../SDNControllerInfoComponent.html | 90 ++ .../SDNControllerInfoComponent.scss | 17 + .../SDNControllerInfoComponent.ts | 96 ++ .../user-settings/UserSettingsComponent.html | 44 + .../user-settings/UserSettingsComponent.scss | 23 + .../user-settings/UserSettingsComponent.ts | 111 ++ src/app/users/UsersComponent.html | 18 + src/app/users/UsersComponent.scss | 17 + src/app/users/UsersComponent.ts | 55 + src/app/users/UsersModule.ts | 76 ++ .../users/add-user/AddEditUserComponent.html | 89 ++ .../users/add-user/AddEditUserComponent.scss | 25 + .../users/add-user/AddEditUserComponent.ts | 255 ++++ .../project-role/ProjectRoleComponent.html | 60 + .../project-role/ProjectRoleComponent.scss | 17 + .../project-role/ProjectRoleComponent.ts | 236 ++++ .../user-details/UserDetailsComponent.html | 36 + .../user-details/UserDetailsComponent.scss | 17 + .../user-details/UserDetailsComponent.ts | 214 ++++ .../clone-package/ClonePackageComponent.html | 33 + .../clone-package/ClonePackageComponent.scss | 17 + .../clone-package/ClonePackageComponent.ts | 180 +++ .../compose-packages/ComposePackages.html | 40 + .../compose-packages/ComposePackages.scss | 17 + .../compose-packages/ComposePackages.ts | 237 ++++ .../ConfirmationTopologyComponent.html | 81 ++ .../ConfirmationTopologyComponent.scss | 24 + .../ConfirmationTopologyComponent.ts | 102 ++ src/app/utilities/delete/DeleteComponent.html | 33 + src/app/utilities/delete/DeleteComponent.scss | 20 + src/app/utilities/delete/DeleteComponent.ts | 199 +++ .../utilities/dragDropUpload/DragDirective.ts | 87 ++ .../edit-packages/EditPackagesComponent.html | 61 + .../edit-packages/EditPackagesComponent.scss | 20 + .../edit-packages/EditPackagesComponent.ts | 318 +++++ src/app/utilities/loader/LoaderComponent.html | 27 + src/app/utilities/loader/LoaderComponent.scss | 89 ++ src/app/utilities/loader/LoaderComponent.ts | 50 + src/app/utilities/loader/LoaderModule.ts | 41 + .../NetsliceInstancesActionComponent.html | 42 + .../NetsliceInstancesActionComponent.scss | 17 + .../NetsliceInstancesActionComponent.ts | 95 ++ .../NetslicePackagesActionComponent.html | 34 + .../NetslicePackagesActionComponent.scss | 17 + .../NetslicePackagesActionComponent.ts | 102 ++ .../NSInstancesActionComponent.html | 56 + .../NSInstancesActionComponent.scss | 17 + .../NSInstancesActionComponent.ts | 181 +++ .../NsPackagesActionComponent.html | 55 + .../NsPackagesActionComponent.scss | 17 + .../NsPackagesActionComponent.ts | 193 +++ .../utilities/page-per-row/PagePerRow.html | 26 + .../utilities/page-per-row/PagePerRow.scss | 23 + src/app/utilities/page-per-row/PagePerRow.ts | 80 ++ .../page-per-row/PagePerRowModule.ts | 42 + src/app/utilities/page-reload/PageReload.html | 21 + src/app/utilities/page-reload/PageReload.scss | 17 + src/app/utilities/page-reload/PageReload.ts | 61 + .../utilities/page-reload/PageReloadModule.ts | 43 + .../PDUInstancesActionComponent.html | 25 + .../PDUInstancesActionComponent.scss | 17 + .../PDUInstancesActionComponent.ts | 88 ++ .../project-link/ProjectLinkComponent.html | 25 + .../project-link/ProjectLinkComponent.scss | 28 + .../project-link/ProjectLinkComponent.ts | 81 ++ .../ProjectsActionComponent.html | 34 + .../ProjectsActionComponent.scss | 17 + .../ProjectsActionComponent.ts | 78 ++ .../roles-action/RolesActionComponent.html | 34 + .../roles-action/RolesActionComponent.scss | 17 + .../roles-action/RolesActionComponent.ts | 79 ++ .../SDNControllerActionComponent.html | 27 + .../SDNControllerActionComponent.scss | 17 + .../SDNControllerActionComponent.ts | 83 ++ .../show-info/ShowInfoComponent.html | 35 + .../show-info/ShowInfoComponent.scss | 17 + .../utilities/show-info/ShowInfoComponent.ts | 216 ++++ .../SwitchProjectComponent.html | 40 + .../SwitchProjectComponent.scss | 17 + .../switch-project/SwitchProjectComponent.ts | 139 +++ .../users-action/UsersActionComponent.html | 42 + .../users-action/UsersActionComponent.scss | 17 + .../users-action/UsersActionComponent.ts | 101 ++ .../VimAccountsActionComponent.html | 46 + .../VimAccountsActionComponent.scss | 17 + .../VimAccountsActionComponent.ts | 104 ++ .../VNFInstancesActionComponent.html | 23 + .../VNFInstancesActionComponent.scss | 17 + .../VNFInstancesActionComponent.ts | 67 + .../VNFPackagesActionComponent.html | 51 + .../VNFPackagesActionComponent.scss | 17 + .../VNFPackagesActionComponent.ts | 187 +++ .../WIMAccountsActionComponent.html | 27 + .../WIMAccountsActionComponent.scss | 17 + .../WIMAccountsActionComponent.ts | 83 ++ .../vim-accounts/VimAccountsComponent.html | 18 + .../vim-accounts/VimAccountsComponent.scss | 17 + src/app/vim-accounts/VimAccountsComponent.ts | 56 + src/app/vim-accounts/VimAccountsModule.ts | 92 ++ .../info-vim/InfoVimComponent.html | 47 + .../info-vim/InfoVimComponent.scss | 17 + .../vim-accounts/info-vim/InfoVimComponent.ts | 445 +++++++ .../NewVimaccountComponent.html | 661 ++++++++++ .../NewVimaccountComponent.scss | 17 + .../new-vimaccount/NewVimaccountComponent.ts | 321 +++++ .../VimAccountDetailsComponent.html | 67 + .../VimAccountDetailsComponent.scss | 73 ++ .../VimAccountDetailsComponent.ts | 476 +++++++ .../wim-accounts/WIMAccountsComponent.html | 18 + .../wim-accounts/WIMAccountsComponent.scss | 17 + src/app/wim-accounts/WIMAccountsComponent.ts | 55 + src/app/wim-accounts/WIMAccountsModule.ts | 77 ++ .../NewWIMAccountComponent.html | 97 ++ .../NewWIMAccountComponent.scss | 17 + .../new-wim-account/NewWIMAccountComponent.ts | 189 +++ .../WIMAccountDetailsComponent.html | 42 + .../WIMAccountDetailsComponent.scss | 17 + .../WIMAccountDetailsComponent.ts | 246 ++++ .../WIMAccountInfoComponent.html | 84 ++ .../WIMAccountInfoComponent.scss | 17 + .../WIMAccountInfoComponent.ts | 96 ++ src/assets/config/rolePermissions.json | 692 +++++++++++ src/assets/i18n/de.json | 479 ++++++++ src/assets/i18n/en.json | 479 ++++++++ src/assets/i18n/es.json | 479 ++++++++ src/assets/i18n/pt.json | 479 ++++++++ src/assets/images/CP-VNF.svg | 26 + src/assets/images/CP.svg | 26 + src/assets/images/INTCP.svg | 26 + src/assets/images/INTVL.svg | 26 + src/assets/images/TICK.svg | 29 + src/assets/images/VDU.svg | 26 + src/assets/images/VL.svg | 26 + src/assets/images/VNFD.svg | 26 + src/assets/images/login_background.jpg | Bin 0 -> 768110 bytes src/assets/images/logo.png | Bin 0 -> 22753 bytes src/assets/images/map-icon.png | Bin 0 -> 1747 bytes src/assets/images/page-not-found.jpg | Bin 0 -> 6956 bytes src/assets/js/tar.js | 432 +++++++ src/assets/scss/app.scss | 688 +++++++++++ src/assets/scss/mixins/_animation.scss | 50 + src/assets/scss/mixins/_background.scss | 30 + src/assets/scss/mixins/_border.scss | 43 + src/assets/scss/mixins/_box-shadow.scss | 34 + src/assets/scss/mixins/_custom.scss | 108 ++ src/assets/scss/mixins/_flex.scss | 47 + src/assets/scss/mixins/_font-weight.scss | 43 + src/assets/scss/mixins/_position.scss | 33 + src/assets/scss/mixins/_rem.scss | 42 + src/assets/scss/mixins/_rounded-corners.scss | 96 ++ src/assets/scss/mixins/_transform.scss | 31 + src/assets/scss/mixins/_transition.scss | 29 + src/assets/scss/mixins/mixin.scss | 30 + src/assets/scss/style.scss | 46 + src/assets/scss/variable.scss | 265 ++++ src/directive/GoToTopDirective.ts | 55 + src/environments/environment.prod.ts | 84 ++ src/environments/environment.ts | 84 ++ src/favicon.ico | Bin 0 -> 10462 bytes src/index.html | 34 + src/karma.conf.js | 48 + src/main.ts | 32 + src/models/CommonModel.ts | 207 ++++ src/models/K8sModel.ts | 127 ++ src/models/MenuModel.ts | 261 ++++ src/models/NSDModel.ts | 168 +++ src/models/NSInstanceModel.ts | 282 +++++ src/models/NSTopologyModel.ts | 57 + src/models/NetworkSliceModel.ts | 113 ++ src/models/PDUInstanceModel.ts | 51 + src/models/ProjectModel.ts | 44 + src/models/RoleModel.ts | 57 + src/models/SDNControllerModel.ts | 75 ++ src/models/UserModel.ts | 68 + src/models/VNFDModel.ts | 274 +++++ src/models/VNFInstanceModel.ts | 81 ++ src/models/VimAccountModel.ts | 136 ++ src/models/WIMAccountModel.ts | 80 ++ src/polyfills.ts | 84 ++ src/services/AcessGuardService.ts | 43 + src/services/AuthGuardService.ts | 61 + src/services/AuthInterceptorService.ts | 126 ++ src/services/AuthenticationService.ts | 195 +++ src/services/DataService.ts | 40 + src/services/DeviceCheckService.ts | 52 + src/services/ProjectService.ts | 114 ++ src/services/RestService.ts | 144 +++ src/services/SharedService.ts | 304 +++++ src/test.ts | 44 + src/tsconfig.app.json | 16 + src/tsconfig.spec.json | 18 + src/tslint.json | 17 + tsconfig.json | 139 +++ tslint.json | 299 +++++ 337 files changed, 33649 insertions(+), 39 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 angular.json create mode 100644 browserslist create mode 100644 docker/Dockerfile create mode 100644 nginx/nginx.conf create mode 100644 package.json create mode 100644 proxy.conf.json create mode 100644 src/app/AppComponent.html create mode 100644 src/app/AppComponent.scss create mode 100644 src/app/AppComponent.ts create mode 100644 src/app/AppModule.ts create mode 100644 src/app/approutes.module.ts create mode 100644 src/app/dashboard/DashboardComponent.html create mode 100644 src/app/dashboard/DashboardComponent.scss create mode 100644 src/app/dashboard/DashboardComponent.ts create mode 100644 src/app/dashboard/DashboardModule.ts create mode 100644 src/app/instances/InstancesComponent.html create mode 100644 src/app/instances/InstancesComponent.scss create mode 100644 src/app/instances/InstancesComponent.ts create mode 100644 src/app/instances/InstancesModule.ts create mode 100644 src/app/instances/netslice-instances/NetsliceInstancesComponent.html create mode 100644 src/app/instances/netslice-instances/NetsliceInstancesComponent.scss create mode 100644 src/app/instances/netslice-instances/NetsliceInstancesComponent.ts create mode 100644 src/app/instances/ns-history-operations/HistoryOperationsComponent.html create mode 100644 src/app/instances/ns-history-operations/HistoryOperationsComponent.scss create mode 100644 src/app/instances/ns-history-operations/HistoryOperationsComponent.ts create mode 100644 src/app/instances/ns-instances/NSInstancesComponent.html create mode 100644 src/app/instances/ns-instances/NSInstancesComponent.scss create mode 100644 src/app/instances/ns-instances/NSInstancesComponent.ts create mode 100644 src/app/instances/ns-primitive/NSPrimitiveComponent.html create mode 100644 src/app/instances/ns-primitive/NSPrimitiveComponent.scss create mode 100644 src/app/instances/ns-primitive/NSPrimitiveComponent.ts create mode 100644 src/app/instances/ns-topology/NSTopologyComponent.html create mode 100644 src/app/instances/ns-topology/NSTopologyComponent.scss create mode 100644 src/app/instances/ns-topology/NSTopologyComponent.ts create mode 100644 src/app/instances/pdu-instances/PDUInstancesComponent.html create mode 100644 src/app/instances/pdu-instances/PDUInstancesComponent.scss create mode 100644 src/app/instances/pdu-instances/PDUInstancesComponent.ts create mode 100644 src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.html create mode 100644 src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.scss create mode 100644 src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.ts create mode 100644 src/app/instances/vnf-instances/VNFInstancesComponent.html create mode 100644 src/app/instances/vnf-instances/VNFInstancesComponent.scss create mode 100644 src/app/instances/vnf-instances/VNFInstancesComponent.ts create mode 100644 src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.html create mode 100644 src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.scss create mode 100644 src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.ts create mode 100644 src/app/k8s/K8sComponent.html create mode 100644 src/app/k8s/K8sComponent.scss create mode 100644 src/app/k8s/K8sComponent.ts create mode 100644 src/app/k8s/K8sModule.ts create mode 100644 src/app/k8s/k8s-action/K8sActionComponent.html create mode 100644 src/app/k8s/k8s-action/K8sActionComponent.scss create mode 100644 src/app/k8s/k8s-action/K8sActionComponent.ts create mode 100644 src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.html create mode 100644 src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.scss create mode 100644 src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.ts create mode 100644 src/app/k8s/k8s-add-repo/K8sAddRepoComponent.html create mode 100644 src/app/k8s/k8s-add-repo/K8sAddRepoComponent.scss create mode 100644 src/app/k8s/k8s-add-repo/K8sAddRepoComponent.ts create mode 100644 src/app/k8s/k8scluster/K8sClusterComponent.html create mode 100644 src/app/k8s/k8scluster/K8sClusterComponent.scss create mode 100644 src/app/k8s/k8scluster/K8sClusterComponent.ts create mode 100644 src/app/k8s/k8srepository/K8sRepositoryComponent.html create mode 100644 src/app/k8s/k8srepository/K8sRepositoryComponent.scss create mode 100644 src/app/k8s/k8srepository/K8sRepositoryComponent.ts create mode 100644 src/app/layouts/LayoutComponent.html create mode 100644 src/app/layouts/LayoutComponent.scss create mode 100644 src/app/layouts/LayoutComponent.ts create mode 100644 src/app/layouts/breadcrumb/BreadcrumbComponent.html create mode 100644 src/app/layouts/breadcrumb/BreadcrumbComponent.scss create mode 100644 src/app/layouts/breadcrumb/BreadcrumbComponent.ts create mode 100644 src/app/layouts/header/HeaderComponent.html create mode 100644 src/app/layouts/header/HeaderComponent.scss create mode 100644 src/app/layouts/header/HeaderComponent.ts create mode 100644 src/app/layouts/sidebar/SidebarComponent.html create mode 100644 src/app/layouts/sidebar/SidebarComponent.scss create mode 100644 src/app/layouts/sidebar/SidebarComponent.ts create mode 100644 src/app/login/LoginComponent.html create mode 100644 src/app/login/LoginComponent.scss create mode 100644 src/app/login/LoginComponent.ts create mode 100644 src/app/packages/PackagesComponent.html create mode 100644 src/app/packages/PackagesComponent.scss create mode 100644 src/app/packages/PackagesComponent.ts create mode 100644 src/app/packages/PackagesModule.ts create mode 100644 src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.html create mode 100644 src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.scss create mode 100644 src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.ts create mode 100644 src/app/packages/instantiate-ns/InstantiateNsComponent.html create mode 100644 src/app/packages/instantiate-ns/InstantiateNsComponent.scss create mode 100644 src/app/packages/instantiate-ns/InstantiateNsComponent.ts create mode 100644 src/app/packages/netslice-template/NetsliceTemplateComponent.html create mode 100644 src/app/packages/netslice-template/NetsliceTemplateComponent.scss create mode 100644 src/app/packages/netslice-template/NetsliceTemplateComponent.ts create mode 100644 src/app/packages/ns-packages/NSPackagesComponent.html create mode 100644 src/app/packages/ns-packages/NSPackagesComponent.scss create mode 100644 src/app/packages/ns-packages/NSPackagesComponent.ts create mode 100644 src/app/packages/ns-packages/ns-composer/NSComposerComponent.html create mode 100644 src/app/packages/ns-packages/ns-composer/NSComposerComponent.scss create mode 100644 src/app/packages/ns-packages/ns-composer/NSComposerComponent.ts create mode 100644 src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.html create mode 100644 src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.scss create mode 100644 src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.ts create mode 100644 src/app/packages/show-content/ShowContentComponent.html create mode 100644 src/app/packages/show-content/ShowContentComponent.scss create mode 100644 src/app/packages/show-content/ShowContentComponent.ts create mode 100644 src/app/packages/vnf-packages/VNFPackagesComponent.html create mode 100644 src/app/packages/vnf-packages/VNFPackagesComponent.scss create mode 100644 src/app/packages/vnf-packages/VNFPackagesComponent.ts create mode 100644 src/app/page-not-found/PageNotFoundComponent.html create mode 100644 src/app/page-not-found/PageNotFoundComponent.scss create mode 100644 src/app/page-not-found/PageNotFoundComponent.ts create mode 100644 src/app/projects/ProjectsComponent.html create mode 100644 src/app/projects/ProjectsComponent.scss create mode 100644 src/app/projects/ProjectsComponent.ts create mode 100644 src/app/projects/ProjectsModule.ts create mode 100644 src/app/projects/project-create-update/ProjectCreateUpdateComponent.html create mode 100644 src/app/projects/project-create-update/ProjectCreateUpdateComponent.scss create mode 100644 src/app/projects/project-create-update/ProjectCreateUpdateComponent.ts create mode 100644 src/app/roles/RolesComponent.html create mode 100644 src/app/roles/RolesComponent.scss create mode 100644 src/app/roles/RolesComponent.ts create mode 100644 src/app/roles/RolesModule.ts create mode 100644 src/app/roles/roles-create-edit/RolesCreateEditComponent.html create mode 100644 src/app/roles/roles-create-edit/RolesCreateEditComponent.scss create mode 100644 src/app/roles/roles-create-edit/RolesCreateEditComponent.ts create mode 100644 src/app/roles/roles-details/RolesDetailsComponent.html create mode 100644 src/app/roles/roles-details/RolesDetailsComponent.scss create mode 100644 src/app/roles/roles-details/RolesDetailsComponent.ts create mode 100644 src/app/sdn-controller/SDNControllerComponent.html create mode 100644 src/app/sdn-controller/SDNControllerComponent.scss create mode 100644 src/app/sdn-controller/SDNControllerComponent.ts create mode 100644 src/app/sdn-controller/SDNControllerModule.ts create mode 100644 src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.html create mode 100644 src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.scss create mode 100644 src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.ts create mode 100644 src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.html create mode 100644 src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.scss create mode 100644 src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.ts create mode 100644 src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.html create mode 100644 src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.scss create mode 100644 src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.ts create mode 100644 src/app/user-settings/UserSettingsComponent.html create mode 100644 src/app/user-settings/UserSettingsComponent.scss create mode 100644 src/app/user-settings/UserSettingsComponent.ts create mode 100644 src/app/users/UsersComponent.html create mode 100644 src/app/users/UsersComponent.scss create mode 100644 src/app/users/UsersComponent.ts create mode 100644 src/app/users/UsersModule.ts create mode 100644 src/app/users/add-user/AddEditUserComponent.html create mode 100644 src/app/users/add-user/AddEditUserComponent.scss create mode 100644 src/app/users/add-user/AddEditUserComponent.ts create mode 100644 src/app/users/project-role/ProjectRoleComponent.html create mode 100644 src/app/users/project-role/ProjectRoleComponent.scss create mode 100644 src/app/users/project-role/ProjectRoleComponent.ts create mode 100644 src/app/users/user-details/UserDetailsComponent.html create mode 100644 src/app/users/user-details/UserDetailsComponent.scss create mode 100644 src/app/users/user-details/UserDetailsComponent.ts create mode 100644 src/app/utilities/clone-package/ClonePackageComponent.html create mode 100644 src/app/utilities/clone-package/ClonePackageComponent.scss create mode 100644 src/app/utilities/clone-package/ClonePackageComponent.ts create mode 100644 src/app/utilities/compose-packages/ComposePackages.html create mode 100644 src/app/utilities/compose-packages/ComposePackages.scss create mode 100644 src/app/utilities/compose-packages/ComposePackages.ts create mode 100644 src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.html create mode 100644 src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.scss create mode 100644 src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.ts create mode 100644 src/app/utilities/delete/DeleteComponent.html create mode 100644 src/app/utilities/delete/DeleteComponent.scss create mode 100644 src/app/utilities/delete/DeleteComponent.ts create mode 100644 src/app/utilities/dragDropUpload/DragDirective.ts create mode 100644 src/app/utilities/edit-packages/EditPackagesComponent.html create mode 100644 src/app/utilities/edit-packages/EditPackagesComponent.scss create mode 100644 src/app/utilities/edit-packages/EditPackagesComponent.ts create mode 100644 src/app/utilities/loader/LoaderComponent.html create mode 100644 src/app/utilities/loader/LoaderComponent.scss create mode 100644 src/app/utilities/loader/LoaderComponent.ts create mode 100644 src/app/utilities/loader/LoaderModule.ts create mode 100644 src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.html create mode 100644 src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.scss create mode 100644 src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.ts create mode 100644 src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.html create mode 100644 src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.scss create mode 100644 src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.ts create mode 100644 src/app/utilities/ns-instances-action/NSInstancesActionComponent.html create mode 100644 src/app/utilities/ns-instances-action/NSInstancesActionComponent.scss create mode 100644 src/app/utilities/ns-instances-action/NSInstancesActionComponent.ts create mode 100644 src/app/utilities/ns-packages-action/NsPackagesActionComponent.html create mode 100644 src/app/utilities/ns-packages-action/NsPackagesActionComponent.scss create mode 100644 src/app/utilities/ns-packages-action/NsPackagesActionComponent.ts create mode 100644 src/app/utilities/page-per-row/PagePerRow.html create mode 100644 src/app/utilities/page-per-row/PagePerRow.scss create mode 100644 src/app/utilities/page-per-row/PagePerRow.ts create mode 100644 src/app/utilities/page-per-row/PagePerRowModule.ts create mode 100644 src/app/utilities/page-reload/PageReload.html create mode 100644 src/app/utilities/page-reload/PageReload.scss create mode 100644 src/app/utilities/page-reload/PageReload.ts create mode 100644 src/app/utilities/page-reload/PageReloadModule.ts create mode 100644 src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.html create mode 100644 src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.scss create mode 100644 src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.ts create mode 100644 src/app/utilities/project-link/ProjectLinkComponent.html create mode 100644 src/app/utilities/project-link/ProjectLinkComponent.scss create mode 100644 src/app/utilities/project-link/ProjectLinkComponent.ts create mode 100644 src/app/utilities/projects-action/ProjectsActionComponent.html create mode 100644 src/app/utilities/projects-action/ProjectsActionComponent.scss create mode 100644 src/app/utilities/projects-action/ProjectsActionComponent.ts create mode 100644 src/app/utilities/roles-action/RolesActionComponent.html create mode 100644 src/app/utilities/roles-action/RolesActionComponent.scss create mode 100644 src/app/utilities/roles-action/RolesActionComponent.ts create mode 100644 src/app/utilities/sdn-controller-action/SDNControllerActionComponent.html create mode 100644 src/app/utilities/sdn-controller-action/SDNControllerActionComponent.scss create mode 100644 src/app/utilities/sdn-controller-action/SDNControllerActionComponent.ts create mode 100644 src/app/utilities/show-info/ShowInfoComponent.html create mode 100644 src/app/utilities/show-info/ShowInfoComponent.scss create mode 100644 src/app/utilities/show-info/ShowInfoComponent.ts create mode 100644 src/app/utilities/switch-project/SwitchProjectComponent.html create mode 100644 src/app/utilities/switch-project/SwitchProjectComponent.scss create mode 100644 src/app/utilities/switch-project/SwitchProjectComponent.ts create mode 100644 src/app/utilities/users-action/UsersActionComponent.html create mode 100644 src/app/utilities/users-action/UsersActionComponent.scss create mode 100644 src/app/utilities/users-action/UsersActionComponent.ts create mode 100644 src/app/utilities/vim-accounts-action/VimAccountsActionComponent.html create mode 100644 src/app/utilities/vim-accounts-action/VimAccountsActionComponent.scss create mode 100644 src/app/utilities/vim-accounts-action/VimAccountsActionComponent.ts create mode 100644 src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.html create mode 100644 src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.scss create mode 100644 src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.ts create mode 100644 src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.html create mode 100644 src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.scss create mode 100644 src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.ts create mode 100644 src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.html create mode 100644 src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.scss create mode 100644 src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.ts create mode 100644 src/app/vim-accounts/VimAccountsComponent.html create mode 100644 src/app/vim-accounts/VimAccountsComponent.scss create mode 100644 src/app/vim-accounts/VimAccountsComponent.ts create mode 100644 src/app/vim-accounts/VimAccountsModule.ts create mode 100644 src/app/vim-accounts/info-vim/InfoVimComponent.html create mode 100644 src/app/vim-accounts/info-vim/InfoVimComponent.scss create mode 100644 src/app/vim-accounts/info-vim/InfoVimComponent.ts create mode 100644 src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.html create mode 100644 src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.scss create mode 100644 src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.ts create mode 100644 src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.html create mode 100644 src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.scss create mode 100644 src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.ts create mode 100644 src/app/wim-accounts/WIMAccountsComponent.html create mode 100644 src/app/wim-accounts/WIMAccountsComponent.scss create mode 100644 src/app/wim-accounts/WIMAccountsComponent.ts create mode 100644 src/app/wim-accounts/WIMAccountsModule.ts create mode 100644 src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.html create mode 100644 src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.scss create mode 100644 src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.ts create mode 100644 src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.html create mode 100644 src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.scss create mode 100644 src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.ts create mode 100644 src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.html create mode 100644 src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.scss create mode 100644 src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.ts create mode 100644 src/assets/config/rolePermissions.json create mode 100644 src/assets/i18n/de.json create mode 100644 src/assets/i18n/en.json create mode 100644 src/assets/i18n/es.json create mode 100644 src/assets/i18n/pt.json create mode 100644 src/assets/images/CP-VNF.svg create mode 100644 src/assets/images/CP.svg create mode 100644 src/assets/images/INTCP.svg create mode 100644 src/assets/images/INTVL.svg create mode 100644 src/assets/images/TICK.svg create mode 100644 src/assets/images/VDU.svg create mode 100644 src/assets/images/VL.svg create mode 100644 src/assets/images/VNFD.svg create mode 100644 src/assets/images/login_background.jpg create mode 100644 src/assets/images/logo.png create mode 100644 src/assets/images/map-icon.png create mode 100644 src/assets/images/page-not-found.jpg create mode 100644 src/assets/js/tar.js create mode 100644 src/assets/scss/app.scss create mode 100644 src/assets/scss/mixins/_animation.scss create mode 100644 src/assets/scss/mixins/_background.scss create mode 100644 src/assets/scss/mixins/_border.scss create mode 100644 src/assets/scss/mixins/_box-shadow.scss create mode 100644 src/assets/scss/mixins/_custom.scss create mode 100644 src/assets/scss/mixins/_flex.scss create mode 100644 src/assets/scss/mixins/_font-weight.scss create mode 100644 src/assets/scss/mixins/_position.scss create mode 100644 src/assets/scss/mixins/_rem.scss create mode 100644 src/assets/scss/mixins/_rounded-corners.scss create mode 100644 src/assets/scss/mixins/_transform.scss create mode 100644 src/assets/scss/mixins/_transition.scss create mode 100644 src/assets/scss/mixins/mixin.scss create mode 100644 src/assets/scss/style.scss create mode 100644 src/assets/scss/variable.scss create mode 100644 src/directive/GoToTopDirective.ts create mode 100644 src/environments/environment.prod.ts create mode 100644 src/environments/environment.ts create mode 100644 src/favicon.ico create mode 100644 src/index.html create mode 100644 src/karma.conf.js create mode 100644 src/main.ts create mode 100644 src/models/CommonModel.ts create mode 100644 src/models/K8sModel.ts create mode 100644 src/models/MenuModel.ts create mode 100644 src/models/NSDModel.ts create mode 100644 src/models/NSInstanceModel.ts create mode 100644 src/models/NSTopologyModel.ts create mode 100644 src/models/NetworkSliceModel.ts create mode 100644 src/models/PDUInstanceModel.ts create mode 100644 src/models/ProjectModel.ts create mode 100644 src/models/RoleModel.ts create mode 100644 src/models/SDNControllerModel.ts create mode 100644 src/models/UserModel.ts create mode 100644 src/models/VNFDModel.ts create mode 100644 src/models/VNFInstanceModel.ts create mode 100644 src/models/VimAccountModel.ts create mode 100644 src/models/WIMAccountModel.ts create mode 100644 src/polyfills.ts create mode 100644 src/services/AcessGuardService.ts create mode 100644 src/services/AuthGuardService.ts create mode 100644 src/services/AuthInterceptorService.ts create mode 100644 src/services/AuthenticationService.ts create mode 100644 src/services/DataService.ts create mode 100644 src/services/DeviceCheckService.ts create mode 100644 src/services/ProjectService.ts create mode 100644 src/services/RestService.ts create mode 100644 src/services/SharedService.ts create mode 100644 src/test.ts create mode 100644 src/tsconfig.app.json create mode 100644 src/tsconfig.spec.json create mode 100644 src/tslint.json create mode 100644 tsconfig.json create mode 100644 tslint.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..3903e75 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,30 @@ +# Copyright 2020 TATA ELXSI +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9a88115 --- /dev/null +++ b/.gitignore @@ -0,0 +1,61 @@ +# Copyright 2020 TATA ELXSI +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc + +# dependencies +/node_modules +package-lock.json + +# profiling files +chrome-profiler-events.json +speed-measure-plugin.json + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db diff --git a/Dockerfile b/Dockerfile index 54f0fd5..e7c221b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,3 @@ -# Copyright 2020 ETSI -# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -13,7 +11,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -FROM ubuntu:18.04 +# This Dockerfile is intented for devops and deb package generation +# +# Use Dockerfile.local for running osm/NBI in a docker container from source +# Use Dockerfile.fromdeb for running osm/NBI in a docker container from last stable package + + +FROM ubuntu:16.04 -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y install git \ - make python3 debhelper python3-setuptools apt-utils +RUN apt-get update && apt-get -y install git make libcurl4-gnutls-dev \ + libgnutls-dev debhelper apt-utils dh-make + + \ No newline at end of file diff --git a/README.md b/README.md index 9f1db33..a2117a0 100644 --- a/README.md +++ b/README.md @@ -1,68 +1,124 @@ -# Project Title -One Paragraph of project description goes here +## Angular Based OSM NG UI +This project focuses on the implementation of a web GUI to interact with the Northbound API of OSM. + +The project is based on ([Angular](https://angular.io/)), a One framework for Mobile & desktop, app-design framework and development platform for creating efficient and sophisticated single-page web apps. + +## Table of Contents + +* [Community](#community) +* [Getting Started](#getting-started) +* [Prerequisites](#prerequisites) +* [Installation](#installation) +* [Running the application](#running-the-application) +* [Check the lint](#check-the-lint) +* [Supported Browsers](#supported-browsers) +* [Deployment](#deployment) +* [Built With](#built-with) +* [Contributing](#contributing) +* [Versioning](#versioning) +* [License](#license) + +## Community + +Contact [kumaran.m@tataelxsi.co.in](mailto:kumaran.m@tataelxsi.co.in), [rajesh.s@tataelxsi.co.in](mailto:rajesh.s@tataelxsi.co.in), [barath.r@tataelxsi.co.in](mailto:barath.r@tataelxsi.co.in) for architecture and design discussions, requests for help, features request and bug reports on NG UI. ## Getting Started -These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system. +Following instructions in the sections below will get you a copy of the project up and running on your local machine for development and testing purposes. See Deployment section for notes on how to deploy the project on a live system. ### Prerequisites -What things you need to install the software and how to install them +Angular Setup, Install, & Build Guide -``` -Give examples -``` +1. Install Node.js from [here](https://nodejs.org/en/download/) +To check if nodejs is installed on your system, type below command. This will help you see the version of nodejs currently installed on your system. +`node -version` +After downloading Node.js, the node package manager (npm) should automatically be installed. Test it out by doing: +`npm --version` +2. Install Angluar CLI. [More details](https://cli.angular.io/) Angular can be installed Globally (or) Locally, +Install Globally +`npm install -g @angular/cli` +Install Locally +`npm install @angular/cli` +After installation done you can check the version using below command. It will display version details of angular-cli +`ng version` -### Installing +We are done with the installation of Angular -A step by step series of examples that tell you how to get a development env running +### Installation -Say what the step will be +Clone the NG UI from the repository -``` -Give the example -``` +`git clone "https://osm.etsi.org/gerrit/osm/NG-UI"` -And repeat +Install the packages -``` -until finished -``` +`cd NG-UI` -End with an example of getting some data out of the system or using it for a little demo +`npm install` -## Running the tests +### Running the application -Explain how to run the automated tests for this system +The following instructions is for running NG UI locally for development purpose, +On the folder project + +Open `proxy.conf.json` + +Add the below code +```typescript +{ + "/osm/*": { + "target": "https://OSM-NBI-IP:9999", + "secure": false, + "logLevel": "info" + } +} ``` -Give an example -``` +To run the application give the below command + +`npm run proxy` +- To Run the NG-UI page Navigate to + +## Check the lint + +To check the typescript lint run the below command + +`npm run lint` + +## Supported Browsers + +- Edge (42.17134.1098.0) and IE 11 (Windows) +- Firefox (75.0)(Ubunutu) +- Firefox (75.0)(Windows) +- Chrome (81.0.4044.92) (Ubunutu) +- Chrome (81.0.4044.122) (Windows) ## Deployment -Add additional notes about how to deploy this on a live system +To deploy the NG UI use the [Dockerfile](Dockerfile) ## Built With -* [Python](www.python.org/) - The language used +* [Angular Frame work](https://angular.io/) - The languages used are Javascript, Typescript, HTML, and SCSS ## Contributing @@ -75,4 +131,3 @@ We use [SemVer](http://semver.org/) for versioning. For the versions available, ## License This project is licensed under the Apache2 License - see the [LICENSE.md](LICENSE) file for details - diff --git a/angular.json b/angular.json new file mode 100644 index 0000000..5420910 --- /dev/null +++ b/angular.json @@ -0,0 +1,145 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "osm": { + "root": "", + "sourceRoot": "src", + "projectType": "application", + "prefix": "app", + "schematics": { + "@schematics/angular:component": { + "styleext": "scss" + } + }, + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser", + "options": { + "outputPath": "dist/osm", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "src/tsconfig.app.json", + "assets": [ + "src/favicon.ico", + "src/assets" + ], + "styles": [ + "src/assets/scss/style.scss", + "node_modules/ol/ol.css", + "node_modules/@fortawesome/fontawesome-free/css/all.min.css" + ], + "scripts": [ + "src/assets/js/tar.js" + ], + "es5BrowserSupport": true + }, + "configurations": { + "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "extractCss": true, + "namedChunks": false, + "aot": true, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "budgets": [ + { + "type": "initial", + "maximumWarning": "2mb", + "maximumError": "5mb" + } + ] + } + } + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "browserTarget": "osm:build" + }, + "configurations": { + "production": { + "browserTarget": "osm:build:production" + } + } + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "osm:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "src/tsconfig.spec.json", + "karmaConfig": "src/karma.conf.js", + "styles": [ + "src/styles.css", + "node_modules/ol/ol.css" + ], + "scripts": [], + "assets": [ + "src/favicon.ico", + "src/assets" + ] + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": [ + "src/tsconfig.app.json", + "src/tsconfig.spec.json" + ], + "exclude": [ + "**/node_modules/**" + ] + } + } + } + }, + "osm-e2e": { + "root": "e2e/", + "projectType": "application", + "prefix": "", + "architect": { + "e2e": { + "builder": "@angular-devkit/build-angular:protractor", + "options": { + "protractorConfig": "e2e/protractor.conf.js", + "devServerTarget": "osm:serve" + }, + "configurations": { + "production": { + "devServerTarget": "osm:serve:production" + } + } + }, + "lint": { + "builder": "@angular-devkit/build-angular:tslint", + "options": { + "tsConfig": "e2e/tsconfig.e2e.json", + "exclude": [ + "**/node_modules/**" + ] + } + } + } + } + }, + "defaultProject": "osm" +} \ No newline at end of file diff --git a/browserslist b/browserslist new file mode 100644 index 0000000..412910d --- /dev/null +++ b/browserslist @@ -0,0 +1,28 @@ +# Copyright 2020 TATA ELXSI +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + +# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries +# +# For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed + +> 0.5% +last 2 versions +Firefox ESR +not dead +not IE 9-11 \ No newline at end of file diff --git a/build-debpkg.sh b/build-debpkg.sh index 9a0960a..07a6d43 100755 --- a/build-debpkg.sh +++ b/build-debpkg.sh @@ -15,8 +15,8 @@ # under the License. -PKG_DIRECTORIES="lib static template" -PKG_FILES="LICENSE README.md" +PKG_DIRECTORIES="nginx src" +PKG_FILES="angular.json browserslist CONTRIBUTING.md package.json proxy.conf.json tsconfig.json tslint.json LICENSE README.md" MDG_NAME=ngui DEB_INSTALL=debian/osm-${MDG_NAME}.install export DEBEMAIL="gerardo.garciadeblas@telefonica.com" @@ -52,5 +52,3 @@ pushd $PKG_DIR dh_make -y --indep --createorig --a -c apache dpkg-buildpackage -uc -us -tc -rfakeroot popd - - diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..78643d4 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,54 @@ +# Copyright 2020 TATA ELXSI +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in), VIJAY NAG (vijaynag.bs@tataelxsi.co.in) + +FROM ubuntu:16.04 +# Installing node dependencies. +RUN apt-get update && apt-get install -y curl xz-utils gnupg2 \ + && apt-get update && apt-get install -y apt-transport-https \ + && curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ + && echo "deb https://deb.nodesource.com/node_10.x xenial main" | tee -a /etc/apt/sources.list.d/nodesource.list \ + && echo "deb-src https://deb.nodesource.com/node_10.x xenial main" | tee -a /etc/apt/sources.list.d/nodesource.list \ + && apt-get update && apt-get install -y nodejs \ + && apt-get install -y nginx + +# Preparing working environment. +RUN mkdir -p /usr/src/osm-angularapp +WORKDIR /usr/src/osm-angularapp + +# Installing dependencies. +COPY ./package.json /usr/src/osm-angularapp/ +RUN npm install + +# Copy osm-angularapp source into image. +COPY ./ /usr/src/osm-angularapp + +# Building app. +RUN npm run build + +# Removing nginx default page. +RUN rm -rf /usr/share/nginx/html/* + +# Copying nginx configuration. +COPY nginx/nginx.conf /etc/nginx/sites-available/default +RUN cp -r /usr/src/osm-angularapp/dist/osm/* /usr/share/nginx/html + +# Exposing ports. +EXPOSE 80 + +# Starting server. +CMD ["nginx", "-g", "daemon off;"] + diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..d686603 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,34 @@ +# Copyright 2020 TATA ELXSI +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html index.htm; + client_max_body_size 15M; + + location /osm { + proxy_pass https://nbi:9999; + proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; + proxy_set_header Accept-Encoding ""; + } + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..fc1d601 --- /dev/null +++ b/package.json @@ -0,0 +1,91 @@ +{ + "name": "osm", + "version": "1.0.2", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build --prod --aot", + "test": "ng test", + "lint": "tslint --type-check --project tsconfig.json -c tslint.json", + "e2e": "ng e2e", + "proxy": "ng serve --proxy-config proxy.conf.json" + }, + "private": true, + "dependencies": { + "@akveo/ng2-completer": "^9.0.1", + "@angular/animations": "~9.1.0", + "@angular/cdk": "^7.3.7", + "@angular/common": "~9.1.0", + "@angular/compiler": "~9.1.0", + "@angular/core": "~9.1.0", + "@angular/flex-layout": "^9.0.0-beta.29", + "@angular/forms": "~9.1.0", + "@angular/localize": "^9.1.2", + "@angular/platform-browser": "~9.1.0", + "@angular/platform-browser-dynamic": "~9.1.0", + "@angular/router": "~9.1.0", + "@ctrl/ngx-codemirror": "^2.2.1", + "@fortawesome/fontawesome-free": "^5.13.0", + "@ng-bootstrap/ng-bootstrap": "^5.3.0", + "@ng-idle/core": "^8.0.0-beta.4", + "@ng-idle/keepalive": "^8.0.0-beta.4", + "@ng-select/ng-select": "^3.7.3", + "@ngx-translate/core": "^12.1.2", + "@ngx-translate/http-loader": "^4.0.0", + "@nomadreservations/ngx-codemirror": "^2.0.0", + "@types/d3": "^5.7.2", + "@types/js-yaml": "^3.12.1", + "@types/jsonpath": "^0.2.0", + "@types/ol": "^5.3.5", + "angular-notifier": "^6.0.1", + "bootstrap": "^4.4.1", + "chart.js": "^2.8.0", + "codemirror": "^5.51.0", + "core-js": "^2.5.4", + "d3": "^5.9.2", + "http-status-codes": "^1.3.2", + "js-untar": "^2.0.0", + "js-yaml": "^3.13.1", + "jsonpath": "^1.0.2", + "ng-sidebar": "^9.2.0", + "ng2-charts": "^3.0.0-beta.5", + "ng2-file-upload": "^1.3.0", + "ng2-smart-table": "^1.6.0", + "ol": "^5.3.3", + "pako": "^1.0.10", + "roboto-fontface": "^0.10.0", + "rxjs": "^6.5.4", + "rxjs-compat": "^6.5.5", + "stream": "0.0.2", + "text-encoding": "^0.7.0", + "tslib": "^1.11.1", + "web-animations-js": "^2.3.2", + "zone.js": "~0.10.3" + }, + "devDependencies": { + "@angular-devkit/build-angular": "~0.901.0", + "@angular/cli": "~9.1.0", + "@angular/compiler-cli": "~9.1.0", + "@angular/language-service": "~9.1.0", + "@types/jasmine": "~2.8.8", + "@types/jasminewd2": "~2.0.3", + "@types/jquery": "^3.3.31", + "@types/node": "^8.10.59", + "codelyzer": "^5.0.1", + "jasmine-core": "~2.99.1", + "jasmine-spec-reporter": "~4.2.1", + "karma": "~3.1.1", + "karma-chrome-launcher": "~2.2.0", + "karma-coverage-istanbul-reporter": "~2.0.1", + "karma-jasmine": "~1.1.2", + "karma-jasmine-html-reporter": "^0.2.2", + "ng2-completer": "^3.0.3", + "node-sass": "^4.14.1", + "protractor": "~5.4.0", + "ts-node": "~7.0.0", + "tslint": "^5.11.0", + "tslint-html-report": "^2.0.3", + "tslint-microsoft-contrib": "^6.1.1", + "typescript": "~3.8.3" + } +} diff --git a/proxy.conf.json b/proxy.conf.json new file mode 100644 index 0000000..e6a63c6 --- /dev/null +++ b/proxy.conf.json @@ -0,0 +1,7 @@ +{ + "/osm/*": { + "target": "https://nbi:9999", + "secure": false, + "logLevel": "info" + } +} \ No newline at end of file diff --git a/src/app/AppComponent.html b/src/app/AppComponent.html new file mode 100644 index 0000000..bee03c8 --- /dev/null +++ b/src/app/AppComponent.html @@ -0,0 +1,19 @@ + + + \ No newline at end of file diff --git a/src/app/AppComponent.scss b/src/app/AppComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/AppComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/AppComponent.ts b/src/app/AppComponent.ts new file mode 100644 index 0000000..04ad8d8 --- /dev/null +++ b/src/app/AppComponent.ts @@ -0,0 +1,123 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ + +/** + * @file App Components + */ +import { Component, HostListener, Injector } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { DEFAULT_INTERRUPTSOURCES, Idle } from '@ng-idle/core'; +import { AuthenticationService } from 'AuthenticationService'; +import { DeviceCheckService } from 'DeviceCheckService'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; + +/** + * Creating component + * @Component takes AppComponent.html as template url + */ +@Component({ + selector: 'app-root', + templateUrl: './AppComponent.html', + styleUrls: ['./AppComponent.scss'] +}) +/** Exporting a class @exports AppComponent */ +export class AppComponent { + /** To inject services @public */ + public injector: Injector; + /** Instance for modal service @public */ + public modalService: NgbModal; + /** Device Check service @private */ + private deviceCheckService: DeviceCheckService; + /** Utilizes auth service for any auth operations @private */ + private authService: AuthenticationService; + /** Handle idle time out service @private */ + private idle: Idle; + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.idle = this.injector.get(Idle); + this.authService = this.injector.get(AuthenticationService); + this.modalService = this.injector.get(NgbModal); + this.deviceCheckService = this.injector.get(DeviceCheckService); + this.handleIdle(); + this.sharedService = this.injector.get(SharedService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.sharedService.fetchOSMVersion(); + } + + /** To handle handleIdle @public */ + public handleIdle(): void { + const idleTime: number = 1200; + const idleTimeOutWarning: number = 5; + // sets an idle timeout in seconds. + this.idle.setIdle(idleTime); + //sets a timeout period in seconds. after idleTime seconds of inactivity, the user will be considered timed out. + this.idle.setTimeout(idleTimeOutWarning); + // sets the default interrupts, in this case, things like clicks, scrolls, touches to the document + this.idle.setInterrupts(DEFAULT_INTERRUPTSOURCES); + this.idle.watch(true); + this.idleTimeOut(); + } + + /** Method to capture idle time out event @public */ + public idleTimeOut(): void { + this.idle.onTimeout.subscribe(() => { + this.idle.stop(); + if (localStorage.getItem('id_token') !== null) { + this.authService.logout(); + } + }); + } + + /** Handling Window's Storage Hostlistener @public */ + @HostListener('window:storage', ['$event']) + public handleLocalStorageEvent(evt: StorageEvent): void { + // On Token Change + if (evt.key === 'token_state' && !isNullOrUndefined(evt.key)) { + if (evt.oldValue !== evt.newValue) { + window.location.reload(); + } + } + // On Langauges Change + if (evt.key === 'languageCode' && !isNullOrUndefined(evt.key)) { + if (evt.oldValue !== evt.newValue) { + window.location.reload(); + } + } + } + + /** Handling Window's POP State Hostlistener @public */ + @HostListener('window:popstate', ['$event']) + public handleOnPOPState(evt: PopStateEvent): void { + this.modalService.dismissAll(); + } + + /** Handling Window's orientationchange Hostlistener @public */ + @HostListener('window:resize', ['$event']) + public onResize(event: Event): void { + this.deviceCheckService.checkDeviceType(); + } +} diff --git a/src/app/AppModule.ts b/src/app/AppModule.ts new file mode 100644 index 0000000..02fa736 --- /dev/null +++ b/src/app/AppModule.ts @@ -0,0 +1,231 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Instance Module file + */ +import { CommonModule, LOCATION_INITIALIZED } from '@angular/common'; +import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from '@angular/common/http'; +import { APP_INITIALIZER, Injector, NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterModule } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { TranslateHttpLoader } from '@ngx-translate/http-loader'; +import { NotifierModule, NotifierOptions } from 'angular-notifier'; +import { AuthInterceptorService } from 'AuthInterceptorService'; +import { HeaderComponent } from 'HeaderComponent'; +import { LayoutComponent } from 'LayoutComponent'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { RestService } from 'RestService'; +import { SidebarComponent } from 'SidebarComponent'; +import { AppComponent } from './AppComponent'; + +import { appRoutes } from './approutes.module'; + +import { DataService } from 'DataService'; +import { ProjectService } from 'ProjectService'; +import { SharedService } from 'SharedService'; + +import { CodemirrorModule } from '@ctrl/ngx-codemirror'; +import { NgSelectModule } from '@ng-select/ng-select'; + +import { NgIdleKeepaliveModule } from '@ng-idle/keepalive'; +import { AuthenticationService } from 'AuthenticationService'; +import { AuthGuardService } from 'AuthGuardService'; +import { BreadcrumbComponent } from 'BreadCrumb'; +import { ComposePackages } from 'ComposePackages'; +import { ConfirmationTopologyComponent } from 'ConfirmationTopology'; +import { DeleteComponent } from 'DeleteComponent'; +import { DeviceCheckService } from 'DeviceCheckService'; +import { GoToTopDirective } from 'GoToTopDirective'; +import { InstantiateNetSliceTemplateComponent } from 'InstantiateNetSliceTemplate'; +import { InstantiateNsComponent } from 'InstantiateNs'; +import { LoaderModule } from 'LoaderModule'; +import { LoginComponent } from 'LoginComponent'; +import { NetsliceInstancesActionComponent } from 'NetsliceInstancesActionComponent'; +import { NetslicePackagesActionComponent } from 'NetslicePackagesAction'; +import { NSInstancesActionComponent } from 'NSInstancesActionComponent'; +import { NsPackagesActionComponent } from 'NsPackagesAction'; +import { PageNotFoundComponent } from 'PageNotFound'; +import { PDUInstancesActionComponent } from 'PDUInstancesActionComponent'; +import { ProjectLinkComponent } from 'ProjectLinkComponent'; +import { ProjectsActionComponent } from 'ProjectsAction'; +import { SDNControllerActionComponent } from 'SDNControllerActionComponent'; +import { ShowInfoComponent } from 'ShowInfoComponent'; +import { SwitchProjectComponent } from 'SwitchProjectComponent'; +import { UsersActionComponent } from 'UsersActionComponent'; +import { UserSettingsComponent } from 'UserSettingsComponent'; +import { VimAccountsActionComponent } from 'VimAccountsAction'; +import { VNFInstancesActionComponent } from 'VNFInstancesActionComponent'; +import { VNFLinkComponent } from 'VNFLinkComponent'; +import { VNFPackagesActionComponent } from 'VNFPackagesAction'; +import { WIMAccountsActionComponent } from 'WIMAccountsAction'; + +/** + * Custom angular notifier options + */ +const customNotifierOptions: NotifierOptions = { + position: { horizontal: { position: 'right' }, vertical: { position: 'top' } }, + behaviour: { autoHide: 3000, onClick: 'hide', onMouseover: 'pauseAutoHide' } +}; + +/** + * An NgModule is a class adorned with the @NgModule decorator function. + * @NgModule takes a metadata object that tells Angular how to compile and run module code. + */ +@NgModule({ + declarations: [ + AppComponent, + LayoutComponent, + HeaderComponent, + SidebarComponent, + LoginComponent, + PageNotFoundComponent, + VNFPackagesActionComponent, + NsPackagesActionComponent, + NSInstancesActionComponent, + VNFInstancesActionComponent, + VNFLinkComponent, + NetsliceInstancesActionComponent, + BreadcrumbComponent, + DeleteComponent, + NetslicePackagesActionComponent, + UsersActionComponent, + VimAccountsActionComponent, + ProjectsActionComponent, + ProjectLinkComponent, + UserSettingsComponent, + ShowInfoComponent, + InstantiateNetSliceTemplateComponent, + InstantiateNsComponent, + ConfirmationTopologyComponent, + ComposePackages, + WIMAccountsActionComponent, + PDUInstancesActionComponent, + SDNControllerActionComponent, + SwitchProjectComponent, + GoToTopDirective + ], + imports: [ + NotifierModule.withConfig(customNotifierOptions), + CommonModule, + BrowserModule, + BrowserAnimationsModule, + FormsModule, + ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), + Ng2SmartTableModule, + CodemirrorModule, + NgSelectModule, + HttpClientModule, + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [HttpClient] + } + }), + NgbModule, + NgSelectModule, + RouterModule.forRoot(appRoutes, { useHash: false }), + NgIdleKeepaliveModule.forRoot(), + LoaderModule + ], + providers: [ + { + provide: APP_INITIALIZER, + useFactory: appInitializerFactory, + deps: [TranslateService, Injector], + multi: true + }, + { + provide: HTTP_INTERCEPTORS, + useClass: AuthInterceptorService, + multi: true + }, + RestService, + AuthenticationService, + AuthGuardService, + DataService, + ProjectService, + SharedService, + DeviceCheckService + ], + bootstrap: [AppComponent], + entryComponents: [ + VNFPackagesActionComponent, + NsPackagesActionComponent, + NSInstancesActionComponent, + VNFInstancesActionComponent, + VNFLinkComponent, + NetsliceInstancesActionComponent, + BreadcrumbComponent, + DeleteComponent, + NetslicePackagesActionComponent, + UsersActionComponent, + VimAccountsActionComponent, + ProjectsActionComponent, + ProjectLinkComponent, + UserSettingsComponent, + ShowInfoComponent, + InstantiateNetSliceTemplateComponent, + InstantiateNsComponent, + ConfirmationTopologyComponent, + ComposePackages, + WIMAccountsActionComponent, + PDUInstancesActionComponent, + SDNControllerActionComponent, + SwitchProjectComponent + ] +}) + +/** Exporting a class @exports AppModule */ +export class AppModule { + /** Variables declared to avoid state-less class */ + private appModule: string; +} + +/** + * HttpLoaderFactory is for translate service of the application. + */ +// tslint:disable:function-name +export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader { + const now: number = new Date().getTime(); + return new TranslateHttpLoader(http, './assets/i18n/', '.json?locale=' + now); +} +/** + * HttpLoaderFactory is for translate service of the application. + */ +// tslint:disable:function-name +export function appInitializerFactory(translate: TranslateService, injector: Injector): Object { + // tslint:disable-next-line: no-any + return async (): Promise => { + await injector.get(LOCATION_INITIALIZED, Promise.resolve(null)); + translate.setDefaultLang('en'); + const languageCode: string = localStorage.getItem('languageCode'); + if (languageCode !== null && languageCode !== undefined && languageCode !== '') { + await translate.use(languageCode).toPromise().catch(() => { + translate.setDefaultLang('en'); + }); + } else { + await translate.use('en').toPromise(); + localStorage.setItem('languageCode', 'en'); + } + }; +} diff --git a/src/app/approutes.module.ts b/src/app/approutes.module.ts new file mode 100644 index 0000000..e2f863c --- /dev/null +++ b/src/app/approutes.module.ts @@ -0,0 +1,114 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Routing Module + */ +import { Routes } from '@angular/router'; +import { AuthGuardService } from 'AuthGuardService'; +import { LayoutComponent } from 'LayoutComponent'; +import { LoginComponent } from 'LoginComponent'; +import { PageNotFoundComponent } from 'PageNotFound'; + +/** Exporting a function using Routes @exports AppRoutes */ +export const appRoutes: Routes = [ + { + path: 'login', + component: LoginComponent + }, + { + path: '', + component: LayoutComponent, + canActivate: [AuthGuardService], + children: [ + { + path: '', + // tslint:disable-next-line: no-any + loadChildren: async (): Promise => import('./dashboard/DashboardModule') + .then((m: typeof import('./dashboard/DashboardModule')) => m.DashboardModule), + canActivate: [AuthGuardService] + }, + { + path: 'packages', + // tslint:disable-next-line: no-any + loadChildren: async (): Promise => import('./packages/PackagesModule') + .then((m: typeof import('./packages/PackagesModule')) => m.PackagesModule), + canActivate: [AuthGuardService] + }, + { + path: 'instances', + // tslint:disable-next-line: no-any + loadChildren: async (): Promise => import('./instances/InstancesModule') + .then((m: typeof import('./instances/InstancesModule')) => m.InstancesModule), + canActivate: [AuthGuardService] + }, + { + path: 'vim', + // tslint:disable-next-line: no-any + loadChildren: async (): Promise => import('./vim-accounts/VimAccountsModule') + .then((m: typeof import('./vim-accounts/VimAccountsModule')) => m.VimAccountsModule), + canActivate: [AuthGuardService] + }, + { + path: 'wim', + // tslint:disable-next-line: no-any + loadChildren: async (): Promise => import('./wim-accounts/WIMAccountsModule') + .then((m: typeof import('./wim-accounts/WIMAccountsModule')) => m.WIMAccountsModule), + canActivate: [AuthGuardService] + }, + { + path: 'sdn', + // tslint:disable-next-line: no-any + loadChildren: async (): Promise => import('./sdn-controller/SDNControllerModule') + .then((m: typeof import('./sdn-controller/SDNControllerModule')) => m.SDNControllerModule), + canActivate: [AuthGuardService] + }, + { + path: 'users', + // tslint:disable-next-line: no-any + loadChildren: async (): Promise => import('./users/UsersModule') + .then((m: typeof import('./users/UsersModule')) => m.UsersModule), + canActivate: [AuthGuardService] + }, + { + path: 'projects', + // tslint:disable-next-line: no-any + loadChildren: async (): Promise => import('./projects/ProjectsModule') + .then((m: typeof import('./projects/ProjectsModule')) => m.ProjectsModule), + canActivate: [AuthGuardService] + }, + { + path: 'roles', + // tslint:disable-next-line: no-any + loadChildren: async (): Promise => import('./roles/RolesModule') + .then((m: typeof import('./roles/RolesModule')) => m.RolesModule), + canActivate: [AuthGuardService] + }, + { + path: 'k8s', + // tslint:disable-next-line: no-any + loadChildren: async (): Promise => import('./k8s/K8sModule') + .then((m: typeof import('./k8s/K8sModule')) => m.K8sModule), + canActivate: [AuthGuardService] + } + ] + }, + { + path: '**', + component: PageNotFoundComponent + } +]; diff --git a/src/app/dashboard/DashboardComponent.html b/src/app/dashboard/DashboardComponent.html new file mode 100644 index 0000000..5b09dc9 --- /dev/null +++ b/src/app/dashboard/DashboardComponent.html @@ -0,0 +1,167 @@ + +
+
+
+
+
+
+
+ {{'PAGE.DASHBOARD.UPTIME' | translate}} {{'PAGE.DASHBOARD.RUNNINGINSTANCES' | translate}} +
+
+ {{'PAGE.DASHBOARD.NOINSTANCES' | translate}} +
+
+ +
+
+
+ +
+
+
+
+ {{'PAGE.DASHBOARD.FAILEDINSTANCES' | translate}} +
+
+
    +
  • + {{'PAGE.DASHBOARD.NOINSTANCES' | translate}}
  • +
  • + {{nsFailedInstance.name}} + + {{nsFailedInstance.name}} + + + + +
  • +
+
+
+ +
+
+
+
+
+
+ +

{{ (nsdPackageCount)?nsdPackageCount:0 }}

+
{{'NSPACKAGES' | translate}}
+ + + +
+
+
+
+ +

{{ (nsInstanceCount)?nsInstanceCount:0 }}

+
{{'NSINSTANCES' | translate}}
+ + + +
+
+
+
+
+
+ +

{{ (vnfdPackageCount)?vnfdPackageCount:0 }}

+
{{'VNFPACKAGES' | translate}}
+ + + +
+
+
+
+ +

{{ (vnfInstanceCount)?vnfInstanceCount:0 }}

+
{{'VNFINSTANCES' | translate}}
+ + + +
+
+
+
+
+
+ +

{{ (vimAccountCount)?vimAccountCount:0 }}

+
{{'VIMACCOUNTS' | translate}}
+ + + +
+
+
+
+ +

{{ (sdnControllerCount)?sdnControllerCount:0 }}

+
{{'SDNCONTROLLER' | translate}}
+ + + +
+
+
+
+
+ +
\ No newline at end of file diff --git a/src/app/dashboard/DashboardComponent.scss b/src/app/dashboard/DashboardComponent.scss new file mode 100644 index 0000000..df3c6b1 --- /dev/null +++ b/src/app/dashboard/DashboardComponent.scss @@ -0,0 +1,160 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ + @import "../../assets/scss/mixins/mixin"; + @import "../../assets/scss/variable"; + $min-height-set: 200px; + .dashboard { + .custom-card { + word-wrap: break-word; + @include box-shadow(0px, 1px, 15px, 0px, rgba(69, 90, 100, 0.1)); + @include transition(all, 0.2s, null, null); + @include roundedCorners(5); + @include border(all, 1, solid, rgba(238, 238, 238, 0.75)); + color: $white; + &.card-hover { + @include transition(all, 0.25s, ease, null); + &:hover { + -moz-transform: translateY(-4px) scale(1.01); + -ms-transform: translateY(-4px) scale(1.01); + -o-transform: translateY(-4px) scale(1.01); + transform: translateY(-4px) scale(1.01); + } + } + &.pink-card { + @include background( + linear-gradient(to left top, #d81b60, #e0306d, #e7407a, #ee4f87, #f55c94), + null, + null, + null, + null + ); + } + &.purple-card { + @include background( + linear-gradient(to left top, #605ca8, #736ebb, #8681ce, #9994e2, #aca7f6), + null, + null, + null, + null + ); + } + &.aqua-card { + @include background( + linear-gradient(to left top, #00c0ef, #00cdf5, #00dafa, #00e6fd, #0af3ff), + null, + null, + null, + null + ); + } + .custom-card-header { + @include background(null, $primary, null, null, null); + @include roundedTop(5); + a { + color: $white; + @include flexbox(flex, space-between, null, null, center, null); + @include padding-value(12, 20, 12, 20); + } + } + .card-body { + @include padding-value(5, 5, 5, 10); + &.list-overflow { + overflow-y: scroll; + .list-group { + .list-group-item { + cursor: default; + @include border(all, 0, solid, $black-coral); + @include border(bottom, 1, solid, rgba(0, 0, 0, 0.125)); + @include padding-value(10, 0, 10, 0); + @include margin-value(0, 0, 0, 0); + color: $gray-600; + i { + cursor: pointer; + @include font(null, 14px, null); + &.activeProjectLink { + cursor: default; + } + } + &:last-child { + @include border(bottom, 0, solid, rgba(0, 0, 0, 0.125)); + } + } + } + &.failed-instances { + max-height: $min-height-set; + } + &.project-list { + max-height: 65vh; + } + } + } + } + .instances { + .graph-section { + min-height: $min-height-set; + @include flexbox(null, center, null, null, null, null); + @include padding-value(10, 10, 10, 10); + .card-title { + color: $gray-600; + } + .instances-canvas { + @include flexbox(none !important, null, null, null, null, null); + &.show-canvas { + @include flexbox(block !important, null, null, null, null, null); + } + #canvas{ + @include wh-value(100%, $min-height-set); + } + } + } + } + .module-counts { + .status-card { + overflow: hidden; + @include wh-value(null, 130px); + @include roundedCorners(4); + @include box-shadow(0px, 5px, 20px, 2px, $transparent-dark-bg); + cursor: pointer; + @include flexbox(null, null, null, null, center, null); + @include padding-value(20, 20, 20, 20); + @include border(all, 0, solid, $gray-80); + i { + @include font(null, 2rem, null); + @include margin-value(0, 0, 8, 0); + } + h6 { + @include font(null, 0.8rem, null); + } + &:hover .link-icon { + @include position_value(null, null, -60px, null, null); + } + .link-icon { + @include background(null, rgba(255, 255, 255, 0.5), null, null, null); + @include position_value(absolute, 0px, -130px, null, null); + @include wh-value(130px, 130px); + @include font(null, 30px, null); + @include padding-value(40, 20, 40, 20); + @include roundedCornersPercentage(50%); + @include transition(all, 0.3s, ease-in-out, null); + i { + @include font(null, 1.875rem, null); + } + } + } + } + } \ No newline at end of file diff --git a/src/app/dashboard/DashboardComponent.ts b/src/app/dashboard/DashboardComponent.ts new file mode 100644 index 0000000..4ab802f --- /dev/null +++ b/src/app/dashboard/DashboardComponent.ts @@ -0,0 +1,402 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file Dashboard Component + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { AuthenticationService } from 'AuthenticationService'; +import { Chart } from 'chart.js'; +import { ERRORDATA } from 'CommonModel'; +import { environment } from 'environment'; +import { NSDDetails } from 'NSDModel'; +import { NSInstanceDetails } from 'NSInstanceModel'; +import { ProjectData, ProjectDetails } from 'ProjectModel'; +import { ProjectService } from 'ProjectService'; +import { RestService } from 'RestService'; +import { Observable, Subscription } from 'rxjs'; +import { SDNControllerModel } from 'SDNControllerModel'; +import { SharedService } from 'SharedService'; +import { ProjectRoleMappings, UserDetail } from 'UserModel'; +import { VimAccountDetails } from 'VimAccountModel'; +import { VNFDDetails } from 'VNFDModel'; +import { VNFInstanceDetails } from 'VNFInstanceModel'; + +/** + * Creating component + * @Component takes DashboardComponent.html as template url + */ +@Component({ + styleUrls: ['./DashboardComponent.scss'], + templateUrl: './DashboardComponent.html' +}) + +/** + * This file created during the angular project creation + */ + +/** Exporting a class @exports DashboardComponent */ +export class DashboardComponent implements OnInit { + /** Invoke service injectors @public */ + public injector: Injector; + + /** handle translate @public */ + public translateService: TranslateService; + + /** Observable holds logined value @public */ + public username$: Observable; + + /** Variables holds admin is logged or not @public */ + public isAdmin: boolean; + + /** List of NS failed Instances @public */ + public nsFailedInstances: {}[] = []; + + /** Setting up count for vnfdPackages @public */ + public vnfdPackageCount: number; + + /** Setting up count for nsdPackage @public */ + public nsdPackageCount: number; + + /** Setting up count for nsInstance @public */ + public nsInstanceCount: number; + + /** Setting up count for vnfInstance @public */ + public vnfInstanceCount: number; + + /** Setting up count for vimAccount @public */ + public vimAccountCount: number; + + /** Setting up count for sdnController @public */ + public sdnControllerCount: number; + + /** Variables holds current project details @public */ + public currentProjectDetails: {}; + + /** Array holds all the projects @public */ + public projectList: {}[] = []; + + /** Array holds all the projects @public */ + public allProjectList: {}[] = []; + + /** Variables holds the selected project @public */ + public selectedProject: Observable; + + /** Check the Instances loading results @public */ + public isCanvasLoadingResults: boolean = true; + + /** Check the Projects loading results @public */ + public isProjectsLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** List of NS Success Instances @private */ + public nsRunningInstance: string[] = []; + + /** Utilizes rest service for any CRUD operations @private */ + private restService: RestService; + + /** Utilizes auth service for any auth operations @private */ + private authService: AuthenticationService; + + /** Used to subscribe vnfdPackage @private */ + private vnfdPackageCountSub: Subscription; + + /** Used to subscribe nsdPackage @private */ + private nsdPackageCountSub: Subscription; + + /** Used to subscribe nsInstance @private */ + private nsInstanceCountSub: Subscription; + + /** Used to subscribe vnfInstance @private */ + private vnfInstanceCountSub: Subscription; + + /** Used to subscribe vimAccount @private */ + private vimAccountCountSub: Subscription; + + /** Used to subscribe sdnController @private */ + private sdnControllerCountSub: Subscription; + + /** No of Hours of NS Success Instances @private */ + private noOfHours: number[] = []; + + /** collects charts objects @private */ + private charts: object = []; + + /** Contains all methods related to projects @private */ + private projectService: ProjectService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Contains NS Instance Details */ + private nsInstancesDataArr: {}[]; + + /** Container created time array @private */ + private createdTimes: string[] = []; + + /** Contains slice limit const @private */ + private sliceLimit: number = 10; + + /** Contians hour converter @private */ + private hourConverter: number = 3600; + + /** Contians color code for chart @private */ + private chartColorPink: string = '#e4397c'; + + /** Contians color code for chart @private */ + private chartColorPurple: string = '#605ca8'; + + /** Contians color code for chart @private */ + private chartColorCyan: string = '#00c0ef'; + + /** Contians color code for chart @private */ + private chartColorBlue: string = '#054C8C'; + + /** Contians color code for chart @private */ + private chartColorYellow: string = '#ffce56'; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.authService = this.injector.get(AuthenticationService); + this.projectService = this.injector.get(ProjectService); + this.sharedService = this.injector.get(SharedService); + this.translateService = this.injector.get(TranslateService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.username$ = this.authService.username; + this.isAdmin = (localStorage.getItem('isAdmin') === 'true') ? true : false; + this.selectedProject = this.authService.ProjectName; + this.checkAdminPrivilege(); + this.getUserAccessedProjects(); + this.getAllProjects(); + this.getVnfdPackageCount(); + this.getNsdPackageCount(); + this.getNsInstanceCount(); + this.getVnfInstanceCount(); + this.getVimAccountCount(); + this.getSDNControllerCount(); + } + + /** Get all the projects @public */ + public getUserAccessedProjects(): void { + this.projectService.getUserProjects().subscribe((projects: UserDetail) => { + const projectList: {}[] = projects.project_role_mappings; + this.projectList = projectList.filter( + (thing: ProjectRoleMappings, i: number, arr: []) => arr + .findIndex((t: ProjectRoleMappings) => t.project_name === thing.project_name) === i + ); + }, (error: Error) => { + // TODO: Handle failure + }); + } + + /** Fetching all the Project in dashboard @public */ + public getAllProjects(): void { + this.isProjectsLoadingResults = true; + this.restService.getResource(environment.PROJECTS_URL).subscribe((projectsData: ProjectDetails[]) => { + this.allProjectList = []; + projectsData.forEach((projectData: ProjectDetails) => { + const projectDataObj: ProjectData = this.generateProjectData(projectData); + this.allProjectList.push(projectDataObj); + }); + this.isProjectsLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isProjectsLoadingResults = false; + }); + } + + /** Generate Projects object from loop and return for the datasource @public */ + public generateProjectData(projectData: ProjectDetails): ProjectData { + return { + projectName: projectData.name, + modificationDate: this.sharedService.convertEpochTime(projectData._admin.modified), + creationDate: this.sharedService.convertEpochTime(projectData._admin.created), + id: projectData._id, + project: projectData._id + }; + } + + /** Function to check admin privilege @public */ + public checkAdminPrivilege(): void { + if (!this.isAdmin) { + this.projectService.getCurrentProjectDetails().subscribe((projectDetails: {}) => { + this.currentProjectDetails = projectDetails; + }, (error: Error) => { + // TODO: Handle failure + }); + } + } + + /** Get VNFD Package details @public */ + public getVnfdPackageCount(): void { + this.vnfdPackageCountSub = this.restService.getResource(environment.VNFPACKAGESCONTENT_URL) + .subscribe((vnfdPackageData: VNFDDetails[]) => { + this.vnfdPackageCount = vnfdPackageData.length; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } + + /** Get NSD Package details @public */ + public getNsdPackageCount(): void { + this.nsdPackageCountSub = this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL) + .subscribe((nsdPackageData: NSDDetails[]) => { + this.nsdPackageCount = nsdPackageData.length; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } + + /** Get NS Instance details @public */ + public getNsInstanceCount(): void { + this.isCanvasLoadingResults = true; + this.nsInstanceCountSub = this.restService.getResource(environment.NSDINSTANCES_URL) + .subscribe((nsInstancesData: NSInstanceDetails[]) => { + this.nsInstancesDataArr = nsInstancesData; + this.nsInstanceCount = nsInstancesData.length; + this.nsInstanceChart(); + this.isCanvasLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isCanvasLoadingResults = false; + }); + } + + /** Get NS Instance chart details @public */ + public nsInstanceChart(): void { + this.nsInstancesDataArr.forEach((nsdInstanceData: NSDDetails) => { + const operationalStatus: string = nsdInstanceData['operational-status']; + const configStatus: string = nsdInstanceData['config-status']; + if (operationalStatus === 'failed' || configStatus === 'failed') { + this.nsFailedInstances.push(nsdInstanceData); + } else if (operationalStatus === 'running' && configStatus === 'configured') { + this.nsRunningInstance.push(nsdInstanceData.name); + this.createdTimes.push(((nsdInstanceData._admin.created).toString()).slice(0, this.sliceLimit)); + } + }); + const now: Date = new Date(); + const currentTime: number = Number((now.getTime().toString().slice(0, this.sliceLimit))); + this.createdTimes.forEach((createdTime: string) => { + this.noOfHours.push((Math.round((currentTime - Number(createdTime)) / this.hourConverter))); + }); + this.drawNsChart(); + } + + /** Prepare and sketch NS instance chart */ + public drawNsChart(): void { + this.charts = new Chart('canvas', { + type: 'bar', + data: { + labels: this.nsRunningInstance, + datasets: [{ + data: this.noOfHours, + label: this.translateService.instant('NOOFHOURS'), + borderColor: [this.chartColorPurple, this.chartColorPink, this.chartColorCyan, + this.chartColorBlue, this.chartColorYellow], + fill: false, + backgroundColor: [this.chartColorPurple, this.chartColorPink, this.chartColorCyan, + this.chartColorBlue, this.chartColorYellow] + }] + }, + options: { + legend: { display: false }, + scales: { + xAxes: [{ + display: true, + ticks: { + // tslint:disable-next-line: no-any + callback: (label: any, index: number, labels: string): string => { + const length: number = 20; + const ending: string = '...'; + if (label.length > length) { + return label.substring(0, length - ending.length) + ending; + } else { + return label; + } + } + }, + scaleLabel: { + display: true, + labelString: this.translateService.instant('INSTANCES') + } + }], + yAxes: [{ + ticks: { + beginAtZero: true + }, + display: true, + scaleLabel: { + display: true, + labelString: this.translateService.instant('NOOFHOURS') + } + }] + } + } + }); + } + + /** Get VNFD instance details @public */ + public getVnfInstanceCount(): void { + this.vnfInstanceCountSub = this.restService.getResource(environment.NSDINSTANCES_URL) + .subscribe((vnfInstanceData: VNFInstanceDetails[]) => { + this.vnfInstanceCount = vnfInstanceData.length; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } + + /** Get VIM account details @public */ + public getVimAccountCount(): void { + this.vimAccountCountSub = this.restService.getResource(environment.VIMACCOUNTS_URL) + .subscribe((vimAccountData: VimAccountDetails[]) => { + this.vimAccountCount = vimAccountData.length; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } + + /** Get SDN Controller Count @public */ + public getSDNControllerCount(): void { + this.sdnControllerCountSub = this.restService.getResource(environment.SDNCONTROLLER_URL) + .subscribe((sdnControllerData: SDNControllerModel[]) => { + this.sdnControllerCount = sdnControllerData.length; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } + + /** + * Lifecyle Hooks the trigger before component is deleted + */ + public ngOnDestroy(): void { + this.vnfdPackageCountSub.unsubscribe(); + this.nsdPackageCountSub.unsubscribe(); + this.nsInstanceCountSub.unsubscribe(); + this.vnfInstanceCountSub.unsubscribe(); + this.vimAccountCountSub.unsubscribe(); + this.sdnControllerCountSub.unsubscribe(); + } +} diff --git a/src/app/dashboard/DashboardModule.ts b/src/app/dashboard/DashboardModule.ts new file mode 100644 index 0000000..4d882e2 --- /dev/null +++ b/src/app/dashboard/DashboardModule.ts @@ -0,0 +1,61 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ + +/** + * @file Dashboard Module + */ +import { CommonModule } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; +import { NgModule } from '@angular/core'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { FormsModule } from '@angular/forms'; +import { RouterModule, Routes } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateModule } from '@ngx-translate/core'; +import { DashboardComponent } from 'DashboardComponent'; +import { LoaderModule } from 'LoaderModule'; +import { ChartsModule } from 'ng2-charts'; + +/** To halndle project information */ +const projectInfo: {} = { title: '{project}', url: '/' }; + +/** const values for dashboard Routes */ +const routes: Routes = [ + { + path: '', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo] + }, + component: DashboardComponent + } +]; +/** + * An NgModule is a class adorned with the @NgModule decorator function. + * @NgModule takes a metadata object that tells Angular how to compile and run module code. + */ +@NgModule({ + imports: [FormsModule, CommonModule, HttpClientModule, FlexLayoutModule, TranslateModule, + ChartsModule, RouterModule.forChild(routes), NgbModule, LoaderModule], + declarations: [DashboardComponent] +}) +/** Exporting a class @exports DashboardModule */ +export class DashboardModule { + /** Variables declared to avoid state-less class */ + private dashboardModule: string; +} diff --git a/src/app/instances/InstancesComponent.html b/src/app/instances/InstancesComponent.html new file mode 100644 index 0000000..06b8876 --- /dev/null +++ b/src/app/instances/InstancesComponent.html @@ -0,0 +1,18 @@ + + diff --git a/src/app/instances/InstancesComponent.scss b/src/app/instances/InstancesComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/instances/InstancesComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/instances/InstancesComponent.ts b/src/app/instances/InstancesComponent.ts new file mode 100644 index 0000000..fe46d8c --- /dev/null +++ b/src/app/instances/InstancesComponent.ts @@ -0,0 +1,55 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file Instance components + */ +import { Component, Injector } from '@angular/core'; +import { Router, RouterEvent } from '@angular/router'; +/** + * Creating component + * @Component takes InstancesComponent.html as template url + */ +@Component({ + selector: 'app-instances', + templateUrl: './InstancesComponent.html', + styleUrls: ['./InstancesComponent.scss'] +}) +/** Exporting a class @exports InstancesComponent */ +export class InstancesComponent { + /** Invoke service injectors @public */ + public injector: Injector; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + // creates packages component + constructor(injector: Injector) { + this.injector = injector; + this.router = this.injector.get(Router); + this.router.events.subscribe((event: RouterEvent) => { + this.redirectToList(event.url); + }); + } + + /** Return to list NS Package List */ + public redirectToList(getURL: string): void { + if (getURL === '/instances') { + this.router.navigate(['/instances/ns']).catch(); + } + } +} diff --git a/src/app/instances/InstancesModule.ts b/src/app/instances/InstancesModule.ts new file mode 100644 index 0000000..7e47d32 --- /dev/null +++ b/src/app/instances/InstancesModule.ts @@ -0,0 +1,132 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file Instance module + */ +import { CommonModule } from '@angular/common'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { FormsModule } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterModule, Routes } from '@angular/router'; +import { CodemirrorModule } from '@ctrl/ngx-codemirror'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddPDUInstancesComponent } from 'AddPDUInstancesComponent'; +import { DataService } from 'DataService'; +import { HistoryOperationsComponent } from 'HistoryOperationsComponent'; +import { InstancesComponent } from 'InstancesComponent'; +import { LoaderModule } from 'LoaderModule'; +import { NetsliceInstancesComponent } from 'NetsliceInstancesComponent'; +import { SidebarModule } from 'ng-sidebar'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { NSInstancesComponent } from 'NSInstancesComponent'; +import { NSPrimitiveComponent } from 'NSPrimitiveComponent'; +import { NSTopologyComponent } from 'NSTopologyComponent'; +import { PagePerRowModule } from 'PagePerRowModule'; +import { PageReloadModule } from 'PageReloadModule'; +import { PDUInstancesComponent } from 'PDUInstancesComponent'; +import { VNFInstancesComponent } from 'VNFInstancesComponent'; + +/** To halndle project information */ +const projectInfo: {} = { title: '{project}', url: '/' }; + +/** Exporting a function using Routes @exports routes */ +const routes: Routes = [ + { + path: '', + component: InstancesComponent, + children: [ + { + path: 'ns', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'NSINSTANCES', url: null }] + }, + component: NSInstancesComponent + }, + { + path: 'vnf', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'VNFINSTANCES', url: null }] + }, + component: VNFInstancesComponent + }, + { + path: 'pdu', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'PDUINSTANCES', url: null }] + }, + component: PDUInstancesComponent + }, + { + path: 'netslice', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'PAGE.DASHBOARD.NETSLICEINSTANCE', url: null }] + }, + component: NetsliceInstancesComponent + }, + { + path: ':type/history-operations/:id', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: '{type}', url: '/instances/{type}' }, { title: '{id}', url: null }] + }, + component: HistoryOperationsComponent + }, + { + path: 'ns/:id', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'NSINSTANCES', url: '/instances/ns' }, { title: '{id}', url: null }] + }, + component: NSTopologyComponent + } + ] + } +]; + +/** + * An NgModule is a class adorned with the @NgModule decorator function. + * @NgModule takes a metadata object that tells Angular how to compile and run module code. + */ +@NgModule({ + imports: [ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), FormsModule, TranslateModule, + CodemirrorModule, CommonModule, Ng2SmartTableModule, FlexLayoutModule, RouterModule.forChild(routes), NgbModule, + NgSelectModule, PagePerRowModule, LoaderModule, SidebarModule.forRoot(), PageReloadModule], + declarations: [InstancesComponent, NSInstancesComponent, VNFInstancesComponent, PDUInstancesComponent, AddPDUInstancesComponent, + NetsliceInstancesComponent, HistoryOperationsComponent, NSTopologyComponent, NSPrimitiveComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + providers: [DataService], + entryComponents: [NSPrimitiveComponent, AddPDUInstancesComponent] +}) +/** Exporting a class @exports InstancesModule */ +export class InstancesModule { + /** Resolves state-less class */ + private instancesModule: string; + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.instancesModule = ''; + } +} diff --git a/src/app/instances/netslice-instances/NetsliceInstancesComponent.html b/src/app/instances/netslice-instances/NetsliceInstancesComponent.html new file mode 100644 index 0000000..e8b3c0c --- /dev/null +++ b/src/app/instances/netslice-instances/NetsliceInstancesComponent.html @@ -0,0 +1,44 @@ + +
+
{{'PAGE.DASHBOARD.NETSLICEINSTANCE' | translate}}
+ + + +
+
+
+ +
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/instances/netslice-instances/NetsliceInstancesComponent.scss b/src/app/instances/netslice-instances/NetsliceInstancesComponent.scss new file mode 100644 index 0000000..0ecd95d --- /dev/null +++ b/src/app/instances/netslice-instances/NetsliceInstancesComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ \ No newline at end of file diff --git a/src/app/instances/netslice-instances/NetsliceInstancesComponent.ts b/src/app/instances/netslice-instances/NetsliceInstancesComponent.ts new file mode 100644 index 0000000..3b9564a --- /dev/null +++ b/src/app/instances/netslice-instances/NetsliceInstancesComponent.ts @@ -0,0 +1,283 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file Netslice Instance Component + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { CONFIGCONSTANT, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { InstantiateNetSliceTemplateComponent } from 'InstantiateNetSliceTemplate'; +import { NetsliceInstancesActionComponent } from 'NetsliceInstancesActionComponent'; +import { NSTInstanceData, NSTInstanceDetails } from 'NetworkSliceModel'; +import { LocalDataSource } from 'ng2-smart-table'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes NetsliceInstancesComponent.html as template url + */ +@Component({ + templateUrl: './NetsliceInstancesComponent.html', + styleUrls: ['./NetsliceInstancesComponent.scss'] +}) + +/** Exporting a class @exports NetsliceInstancesComponent */ +export class NetsliceInstancesComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** handle translate @public */ + public translateService: TranslateService; + + /** Columns list of the smart table @public */ + public columnLists: object = {}; + + /** Settings for smarttable to populate the table with columns @public */ + public settings: object = {}; + + /** Datasource instance inititated @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** Datasource table Data for the NST @public */ + public nstInstanceData: NSTInstanceData[] = []; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** operational State init data @public */ + public operationalStateFirstStep: string = CONFIGCONSTANT.operationalStateFirstStep; + + /** operational State running data @public */ + public operationalStateSecondStep: string = CONFIGCONSTANT.operationalStateSecondStep; + + /** operational State failed data @public */ + public operationalStateThirdStep: string = CONFIGCONSTANT.operationalStateThirdStep; + + /** Config State init data @public */ + public configStateFirstStep: string = CONFIGCONSTANT.configStateFirstStep; + + /** Config State init data @public */ + public configStateSecondStep: string = CONFIGCONSTANT.configStateSecondStep; + + /** Config State init data @public */ + public configStateThirdStep: string = CONFIGCONSTANT.configStateThirdStep; + + /** config status assign @public */ + public configStatusCheck: string; + + /** To consume REST API calls @private */ + private dataService: DataService; + + /** Utilizes rest service for any CRUD operations @public */ + private restService: RestService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + this.modalService = this.injector.get(NgbModal); + this.dataService = this.injector.get(DataService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.generateTableColumn(); + this.generateTableSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** smart table listing manipulation @private */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** smart table listing manipulation @private */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'net-slice-instance' }); + this.dataService.changeMessage(event.data); + } + + /** Instantiate Net Slice using modalservice @public */ + public instantiateNetSlice(): void { + const modalRef: NgbModalRef = this.modalService.open(InstantiateNetSliceTemplateComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.generateData(); + } + }).catch(); + } + + /** Generate smart table row title and filters @public */ + public generateTableSettings(): void { + this.settings = { + columns: this.columnLists, + actions: { add: false, edit: false, delete: false, position: 'right' }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** Generate smart table row title and filters @public */ + public generateTableColumn(): void { + this.columnLists = { + name: { title: this.translateService.instant('NAME'), width: '15%', sortDirection: 'asc' }, + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '15%' }, + NstName: { title: this.translateService.instant('NSTNAME'), width: '15%' }, + OperationalStatus: { + type: 'html', + title: this.translateService.instant('OPERATIONALSTATUS'), + width: '15%', + filter: { + type: 'list', + config: { + selectText: 'Select', + list: [ + { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep }, + { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep }, + { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep } + ] + } + }, + valuePrepareFunction: (cell: NSTInstanceData, row: NSTInstanceData): string => { + if (row.OperationalStatus === this.operationalStateFirstStep) { + return ` + + `; + } else if (row.OperationalStatus === this.operationalStateSecondStep) { + return ` + + `; + } else if (row.OperationalStatus === this.operationalStateThirdStep) { + return ` + + `; + } else { + return `${row.OperationalStatus}`; + } + } + }, + ConfigStatus: { + type: 'html', + title: this.translateService.instant('CONFIGSTATUS'), + width: '15%', + filter: { + type: 'list', + config: { + selectText: 'Select', + list: [ + { value: this.configStateFirstStep, title: this.configStateFirstStep }, + { value: this.configStateSecondStep, title: this.configStateSecondStep }, + { value: this.configStateThirdStep, title: this.configStateThirdStep } + ] + } + }, + valuePrepareFunction: (cell: NSTInstanceData, row: NSTInstanceData): string => { + if (row.ConfigStatus === this.configStateFirstStep) { + return ` + + `; + } else if (row.ConfigStatus === this.configStateSecondStep) { + return ` + + `; + } else if (row.ConfigStatus === this.configStateThirdStep) { + return ` + + `; + } else { + return `${row.ConfigStatus}`; + } + } + }, + DetailedStatus: { title: this.translateService.instant('DETAILEDSTATUS'), width: '15%' }, + Actions: { + name: 'Action', width: '10%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom', + valuePrepareFunction: (cell: NSTInstanceData, row: NSTInstanceData): NSTInstanceData => row, + renderComponent: NetsliceInstancesActionComponent + } + }; + } + + /** generateData initiate the net-slice-instance list @public */ + public generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.NETWORKSLICEINSTANCESCONTENT_URL) + .subscribe((netSliceInstancesData: NSTInstanceDetails[]) => { + this.nstInstanceData = []; + netSliceInstancesData.forEach((netSliceInstanceData: NSTInstanceDetails) => { + if (netSliceInstanceData['config-status'] !== undefined) { + this.configStatusCheck = netSliceInstanceData['config-status']; + } else { + this.configStatusCheck = netSliceInstanceData['operational-status']; + } + const netSliceDataObj: NSTInstanceData = { + name: netSliceInstanceData.name, + identifier: netSliceInstanceData.id, + NstName: netSliceInstanceData['nst-ref'], + OperationalStatus: netSliceInstanceData['operational-status'], + ConfigStatus: this.configStatusCheck, + DetailedStatus: netSliceInstanceData['detailed-status'] + }; + this.nstInstanceData.push(netSliceDataObj); + }); + if (this.nstInstanceData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.nstInstanceData).then((data: {}) => { + this.isLoadingResults = false; + }).catch(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } +} diff --git a/src/app/instances/ns-history-operations/HistoryOperationsComponent.html b/src/app/instances/ns-history-operations/HistoryOperationsComponent.html new file mode 100644 index 0000000..7890a13 --- /dev/null +++ b/src/app/instances/ns-history-operations/HistoryOperationsComponent.html @@ -0,0 +1,36 @@ + +
+
{{'HISTORYOFOPERATIONS' | translate}}
+
+
+
+ +
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/instances/ns-history-operations/HistoryOperationsComponent.scss b/src/app/instances/ns-history-operations/HistoryOperationsComponent.scss new file mode 100644 index 0000000..fdec4ed --- /dev/null +++ b/src/app/instances/ns-history-operations/HistoryOperationsComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ diff --git a/src/app/instances/ns-history-operations/HistoryOperationsComponent.ts b/src/app/instances/ns-history-operations/HistoryOperationsComponent.ts new file mode 100644 index 0000000..a0cac86 --- /dev/null +++ b/src/app/instances/ns-history-operations/HistoryOperationsComponent.ts @@ -0,0 +1,265 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file NS History Of Operations Component + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Router } from '@angular/router'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { CONFIGCONSTANT, ERRORDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import * as HttpStatus from 'http-status-codes'; +import { LocalDataSource } from 'ng2-smart-table'; +import { NSDInstanceData } from 'NSInstanceModel'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; +import { ShowInfoComponent } from 'ShowInfoComponent'; + +/** + * Creating component + * @Component takes HistoryOperationsComponent.html as template url + */ +@Component({ + templateUrl: './HistoryOperationsComponent.html', + styleUrls: ['./HistoryOperationsComponent.scss'] +}) +/** Exporting a class @exports HistoryOperationsComponent */ +export class HistoryOperationsComponent implements OnInit { + /** Injector to invoke other services @public */ + public injector: Injector; + + /** NS Instance array @public */ + public nsAndnstInstanceData: object[] = []; + + /** Datasource instance @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** Instance component are stored in settings @public */ + public settings: {} = {}; + + /** Contains objects for smart table title and filter settings @public */ + public columnList: {} = {}; + + /** Variable handles the page name @public */ + public page: string; + + /** Variable handles the title name @public */ + public titleName: string; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** History State init data @public */ + public historyStateFirstStep: string = CONFIGCONSTANT.historyStateFirstStep; + + /** History State running data @public */ + public historyStateSecondStep: string = CONFIGCONSTANT.historyStateSecondStep; + + /** History State failed data @public */ + public historyStateThirdStep: string = CONFIGCONSTANT.historyStateThirdStep; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Utilizes rest service for any CRUD operations @private */ + private restService: RestService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private activatedRoute: ActivatedRoute; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** variables contains paramsID @private */ + private paramsID: string; + + /** variables contains paramsID @private */ + private paramsType: string; + + /** variables conatins URL of the History operations @public */ + private historyURL: string; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + /** Service holds the router information @private */ + private router: Router; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.activatedRoute = this.injector.get(ActivatedRoute); + this.modalService = this.injector.get(NgbModal); + this.translateService = this.injector.get(TranslateService); + this.router = this.injector.get(Router); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + // tslint:disable-next-line:no-backbone-get-set-outside-model + this.paramsID = this.activatedRoute.snapshot.paramMap.get('id'); + // tslint:disable-next-line:no-backbone-get-set-outside-model + this.paramsType = this.activatedRoute.snapshot.paramMap.get('type'); + if (this.paramsType === 'ns') { + this.historyURL = environment.NSHISTORYOPERATIONS_URL + '/?nsInstanceId=' + this.paramsID; + this.page = 'ns-history-operation'; + this.titleName = 'INSTANCEDETAILS'; + } else if (this.paramsType === 'netslice') { + this.historyURL = environment.NSTHISTORYOPERATIONS_URL + '/?netsliceInstanceId=' + this.paramsID; + this.page = 'nst-history-operation'; + this.titleName = 'INSTANCEDETAILS'; + } + this.generateTableColumn(); + this.generateTableSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** Generate smart table row title and filters @public */ + public generateTableSettings(): void { + this.settings = { + columns: this.columnList, + actions: { + add: false, edit: false, delete: false, position: 'right', + custom: [{ + name: 'showInformation', title: ''}] + }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** Generate smart table row title and filters @public */ + public generateTableColumn(): void { + this.columnList = { + id: { title: this.translateService.instant('ID'), width: '30%' }, + type: { title: this.translateService.instant('TYPE'), width: '20%' }, + state: { + type: 'html', title: this.translateService.instant('OPERATIONSTATE'), width: '15%', + filter: { + type: 'list', + config: { + selectText: 'Select', + list: [ + { value: this.historyStateFirstStep, title: this.historyStateFirstStep }, + { value: this.historyStateSecondStep, title: this.historyStateSecondStep }, + { value: this.historyStateThirdStep, title: this.historyStateThirdStep } + ] + } + }, + valuePrepareFunction: (cell: NSDInstanceData, row: NSDInstanceData): string => { + if (row.state === this.historyStateFirstStep) { + return ` + + `; + } else if (row.state === this.historyStateSecondStep) { + return ` + + `; + } else if (row.state === this.historyStateThirdStep) { + return ` + + `; + } else { + return `${row.state}`; + } + } + }, + startTime: { title: this.translateService.instant('STARTTIME'), width: '15%' }, + statusEnteredTime: { title: this.translateService.instant('STATUSENTEREDTIME'), width: '15%' } + }; + } + + /** smart table listing manipulation @public */ + public onUserRowSelect(event: MessageEvent): void { + this.dataService.changeMessage(event.data); + } + /** smart table listing manipulation @public */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + /** show information methods modal with ns history info */ + public showInformation(event: MessageEvent): void { + this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = { + id: event.data.id, + page: this.page, + titleName: this.titleName + }; + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } + + /** generateData initiate the ns-instance list @private */ + private generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(this.historyURL).subscribe((nsdInstancesData: {}[]) => { + this.nsAndnstInstanceData = []; + nsdInstancesData.forEach((nsdAndnstInstanceData: NSDInstanceData) => { + const nsAndnstDataObj: {} = { + id: nsdAndnstInstanceData.id, + type: nsdAndnstInstanceData.lcmOperationType, + state: nsdAndnstInstanceData.operationState, + startTime: this.sharedService.convertEpochTime(nsdAndnstInstanceData.startTime), + statusEnteredTime: this.sharedService.convertEpochTime(nsdAndnstInstanceData.statusEnteredTime) + }; + this.nsAndnstInstanceData.push(nsAndnstDataObj); + }); + + if (this.nsAndnstInstanceData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.nsAndnstInstanceData).then((data: {}) => { + //empty block + }).catch(); + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) { + this.router.navigateByUrl('404', { skipLocationChange: true }).catch(); + } else { + this.restService.handleError(error, 'get'); + } + }); + } +} diff --git a/src/app/instances/ns-instances/NSInstancesComponent.html b/src/app/instances/ns-instances/NSInstancesComponent.html new file mode 100644 index 0000000..6047a2f --- /dev/null +++ b/src/app/instances/ns-instances/NSInstancesComponent.html @@ -0,0 +1,43 @@ + +
+
{{'NSINSTANCES' | translate}}
+ + + +
+
+
+ +
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/instances/ns-instances/NSInstancesComponent.scss b/src/app/instances/ns-instances/NSInstancesComponent.scss new file mode 100644 index 0000000..0ecd95d --- /dev/null +++ b/src/app/instances/ns-instances/NSInstancesComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ \ No newline at end of file diff --git a/src/app/instances/ns-instances/NSInstancesComponent.ts b/src/app/instances/ns-instances/NSInstancesComponent.ts new file mode 100644 index 0000000..07184da --- /dev/null +++ b/src/app/instances/ns-instances/NSInstancesComponent.ts @@ -0,0 +1,278 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file NS Instance Component + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { CONFIGCONSTANT, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { InstantiateNsComponent } from 'InstantiateNs'; +import { LocalDataSource } from 'ng2-smart-table'; +import { NSDInstanceData, NSInstanceDetails } from 'NSInstanceModel'; +import { NSInstancesActionComponent } from 'NSInstancesActionComponent'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes NSInstancesComponent.html as template url + */ +@Component({ + templateUrl: './NSInstancesComponent.html', + styleUrls: ['./NSInstancesComponent.scss'] +}) +/** Exporting a class @exports NSInstancesComponent */ +export class NSInstancesComponent implements OnInit { + /** Injector to invoke other services @public */ + public injector: Injector; + + /** NS Instance array @public */ + public nsInstanceData: object[] = []; + + /** Datasource instance @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** SelectedRows array @public */ + public selectedRows: object[] = []; + + /** Selected list array @public */ + public selectList: object[] = []; + + /** Instance component are stored in settings @public */ + public settings: {} = {}; + + /** Contains objects for menu settings @public */ + public columnList: {} = {}; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** operational State init data @public */ + public operationalStateFirstStep: string = CONFIGCONSTANT.operationalStateFirstStep; + + /** operational State running data @public */ + public operationalStateSecondStep: string = CONFIGCONSTANT.operationalStateSecondStep; + + /** operational State failed data @public */ + public operationalStateThirdStep: string = CONFIGCONSTANT.operationalStateThirdStep; + + /** Config State init data @public */ + public configStateFirstStep: string = CONFIGCONSTANT.configStateFirstStep; + + /** Config State init data @public */ + public configStateSecondStep: string = CONFIGCONSTANT.configStateSecondStep; + + /** Config State init data @public */ + public configStateThirdStep: string = CONFIGCONSTANT.configStateThirdStep; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Utilizes rest service for any CRUD operations @private */ + private restService: RestService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.translateService = this.injector.get(TranslateService); + this.modalService = this.injector.get(NgbModal); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.generateTableColumn(); + this.generateTableSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** Generate smart table row title and filters @public */ + public generateTableSettings(): void { + this.settings = { + columns: this.columnList, + actions: { add: false, edit: false, delete: false, position: 'right' }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** Generate smart table row title and filters @public */ + public generateTableColumn(): void { + this.columnList = { + name: { title: this.translateService.instant('NAME'), width: '15%', sortDirection: 'asc' }, + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' }, + NsdName: { title: this.translateService.instant('NSDNAME'), width: '15%' }, + OperationalStatus: { + title: this.translateService.instant('OPERATIONALSTATUS'), width: '10%', type: 'html', + filter: { + type: 'list', + config: { + selectText: 'Select', + list: [ + { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep }, + { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep }, + { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep } + ] + } + }, + valuePrepareFunction: (cell: NSDInstanceData, row: NSDInstanceData): string => { + if (row.OperationalStatus === this.operationalStateFirstStep) { + return ` + + `; + } else if (row.OperationalStatus === this.operationalStateSecondStep) { + return ` + + `; + } else if (row.OperationalStatus === this.operationalStateThirdStep) { + return ` + + `; + } else { + return `${row.OperationalStatus}`; + } + } + }, + ConfigStatus: { + title: this.translateService.instant('CONFIGSTATUS'), width: '10%', type: 'html', + filter: { + type: 'list', + config: { + selectText: 'Select', + list: [ + { value: this.configStateFirstStep, title: this.configStateFirstStep }, + { value: this.configStateSecondStep, title: this.configStateSecondStep }, + { value: this.configStateThirdStep, title: this.configStateThirdStep } + ] + } + }, + valuePrepareFunction: (cell: NSDInstanceData, row: NSDInstanceData): string => { + if (row.ConfigStatus === this.configStateFirstStep) { + return ` + + `; + } else if (row.ConfigStatus === this.configStateSecondStep) { + return ` + + `; + } else if (row.ConfigStatus === this.configStateThirdStep) { + return ` + + `; + } else { + return `${row.ConfigStatus}`; + } + } + }, + DetailedStatus: { title: this.translateService.instant('DETAILEDSTATUS'), width: '15%' }, + Actions: { + name: 'Action', width: '15%', filter: false, sort: false, type: 'custom', + title: this.translateService.instant('ACTIONS'), + valuePrepareFunction: (cell: NSDInstanceData, row: NSDInstanceData): NSDInstanceData => row, + renderComponent: NSInstancesActionComponent + } + }; + } + + /** generateData initiate the ns-instance list @public */ + public generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.NSDINSTANCES_URL).subscribe((nsdInstancesData: NSInstanceDetails[]) => { + this.nsInstanceData = []; + nsdInstancesData.forEach((nsdInstanceData: NSInstanceDetails) => { + const nsDataObj: NSDInstanceData = { + name: nsdInstanceData.name, + identifier: nsdInstanceData.id, + NsdName: nsdInstanceData['nsd-name-ref'], + OperationalStatus: nsdInstanceData['operational-status'], + ConfigStatus: nsdInstanceData['config-status'], + DetailedStatus: nsdInstanceData['detailed-status'], + memberIndex: nsdInstanceData.nsd['constituent-vnfd'], + nsConfig: nsdInstanceData.nsd['ns-configuration'] + }; + this.nsInstanceData.push(nsDataObj); + }); + if (this.nsInstanceData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.nsInstanceData).then((data: {}) => { + this.isLoadingResults = false; + }).catch(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** smart table listing manipulation @public */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** smart table listing manipulation @public */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'ns-instance' }); + this.dataService.changeMessage(event.data); + } + + /** Instantiate NS using modalservice @public */ + public instantiateNS(): void { + const modalRef: NgbModalRef = this.modalService.open(InstantiateNsComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.generateData(); + } + }).catch(); + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } +} diff --git a/src/app/instances/ns-primitive/NSPrimitiveComponent.html b/src/app/instances/ns-primitive/NSPrimitiveComponent.html new file mode 100644 index 0000000..646c8ae --- /dev/null +++ b/src/app/instances/ns-primitive/NSPrimitiveComponent.html @@ -0,0 +1,92 @@ + + +
+ + +
+ \ No newline at end of file diff --git a/src/app/instances/ns-primitive/NSPrimitiveComponent.scss b/src/app/instances/ns-primitive/NSPrimitiveComponent.scss new file mode 100644 index 0000000..edad97f --- /dev/null +++ b/src/app/instances/ns-primitive/NSPrimitiveComponent.scss @@ -0,0 +1,34 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +@import '../../../assets/scss/mixins/mixin'; +@import '../../../assets/scss/variable'; + +.primitive-params-head { + @include padding-value(10, 10, 10, 10); + line-height: 2em; + .btn-params { + @include border(all, 1, solid, $white); + } +} + +.remove-params { + display: flex; + align-items: center; + font-size: 20px; + cursor: pointer; +} \ No newline at end of file diff --git a/src/app/instances/ns-primitive/NSPrimitiveComponent.ts b/src/app/instances/ns-primitive/NSPrimitiveComponent.ts new file mode 100644 index 0000000..02269d3 --- /dev/null +++ b/src/app/instances/ns-primitive/NSPrimitiveComponent.ts @@ -0,0 +1,271 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file NS Instance Primitive Component + */ +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, URLPARAMS } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { NSData } from 'NSDModel'; +import { NSPrimitiveParams } from 'NSInstanceModel'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; + +/** + * Creating component + * @Component takes NSPrimitiveComponent.html as template url + */ +@Component({ + templateUrl: './NSPrimitiveComponent.html', + styleUrls: ['./NSPrimitiveComponent.scss'] +}) +/** Exporting a class @exports NSPrimitiveComponent */ +export class NSPrimitiveComponent implements OnInit { + /** Form valid on submit trigger @public */ + public submitted: boolean = false; + + /** To inject services @public */ + public injector: Injector; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** FormGroup instance added to the form @ html @public */ + public primitiveForm: FormGroup; + + /** Primitive params array @public */ + public primitiveParams: FormArray; + + /** Variable set for twoway binding @public */ + public nsdId: string; + + /** Check the loading results @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Contains list of primitive parameter @public */ + public primitiveParameter: {}[] = []; + + /** Input contains component objects @public */ + @Input() public params: URLPARAMS; + + /** Contains list of primitive actions @public */ + public primitiveList: {}[]; + + /** Contains objects that is used to hold types of primitive @public */ + public primitiveTypeList: {}[] = []; + + /** Model value used to hold selected primitive type @public */ + public primitiveType: string; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Utilizes rest service for any CRUD operations @private */ + private restService: RestService; + + /** packages data service collections @private */ + private dataService: DataService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Contains objects that is used to convert key/value pair @private */ + private objectPrimitiveParams: {} = {}; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.translateService = this.injector.get(TranslateService); + this.notifierService = this.injector.get(NotifierService); + this.sharedService = this.injector.get(SharedService); + this.activeModal = this.injector.get(NgbActiveModal); + this.formBuilder = this.injector.get(FormBuilder); + this.primitiveTypeList = [{ title: this.translateService.instant('VNFPRIMITIVE'), value: 'VNF_Primitive' }]; + this.primitiveType = 'VNF_Primitive'; + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + /** Setting up initial value for NSD */ + this.dataService.currentMessage.subscribe((event: NSData) => { + if (event.identifier !== undefined || event.identifier !== '' || event.identifier !== null) { + this.nsdId = event.identifier; + } + }); + if (!isNullOrUndefined(this.params.nsConfig)) { + this.primitiveTypeList.push({ title: this.translateService.instant('NSPRIMITIVE'), value: 'NS_Primitive' }); + } + this.initializeForm(); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.primitiveForm.controls; } + + /** initialize Forms @public */ + public initializeForm(): void { + this.primitiveForm = this.formBuilder.group({ + primitive: [null, [Validators.required]], + vnf_member_index: [null, [Validators.required]], + primitive_params: this.formBuilder.array([this.primitiveParamsBuilder()]) + }); + } + + /** Generate primitive params @public */ + public primitiveParamsBuilder(): FormGroup { + return this.formBuilder.group({ + primitive_params_name: [null, [Validators.required]], + primitive_params_value: ['', [Validators.required]] + }); + } + + /** Handle FormArray Controls @public */ + public getControls(): AbstractControl[] { + return (this.getFormControl('primitive_params') as FormArray).controls; + } + + /** Push all primitive params on user's action @public */ + public createPrimitiveParams(): void { + this.primitiveParams = this.getFormControl('primitive_params') as FormArray; + this.primitiveParams.push(this.primitiveParamsBuilder()); + } + + /** Remove primitive params on user's action @public */ + public removePrimitiveParams(index: number): void { + this.primitiveParams.removeAt(index); + } + + /** Execute NS Primitive @public */ + public execNSPrimitive(): void { + this.submitted = true; + this.objectPrimitiveParams = {}; + this.sharedService.cleanForm(this.primitiveForm); + if (this.primitiveForm.invalid) { return; } // Proceed, onces form is valid + this.primitiveForm.value.primitive_params.forEach((params: NSPrimitiveParams) => { + if (params.primitive_params_name !== null && params.primitive_params_value !== '') { + this.objectPrimitiveParams[params.primitive_params_name] = params.primitive_params_value; + } + }); + //Prepare primitive params + const primitiveParamsPayLoads: {} = { + primitive: this.primitiveForm.value.primitive, + primitive_params: this.objectPrimitiveParams + }; + if (this.primitiveType === 'VNF_Primitive') { + // tslint:disable-next-line: no-string-literal + primitiveParamsPayLoads['vnf_member_index'] = this.primitiveForm.value.vnf_member_index; + } + const apiURLHeader: APIURLHEADER = { + url: environment.NSDINSTANCES_URL + '/' + this.nsdId + '/action' + }; + this.isLoadingResults = true; + this.restService.postResource(apiURLHeader, primitiveParamsPayLoads).subscribe((result: {}) => { + this.activeModal.dismiss(); + this.notifierService.notify('success', this.translateService.instant('PAGE.NSPRIMITIVE.EXECUTEDSUCCESSFULLY')); + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'post'); + }); + } + /** Primitive type change event @public */ + public primitiveTypeChange(data: { value: string }): void { + this.primitiveList = []; + this.primitiveParameter = []; + this.initializeForm(); + if (data.value === 'NS_Primitive') { + this.primitiveList = !isNullOrUndefined(this.params.nsConfig['config-primitive']) ? + this.params.nsConfig['config-primitive'] : []; + this.getFormControl('vnf_member_index').setValidators([]); + } + } + /** Member index change event */ + public indexChange(data: {}): void { + if (data) { + this.getVnfdInfo(data['vnfd-id-ref']); + } else { + this.primitiveList = []; + this.getFormControl('primitive').setValue(null); + this.primitiveParameter = []; + } + } + /** Primivtive change event */ + public primitiveChange(data: { parameter: {}[] }): void { + this.primitiveParameter = []; + const formArr: FormArray = this.getFormControl('primitive_params') as FormArray; + formArr.controls = []; + this.createPrimitiveParams(); + if (data) { + this.updatePrimitive(data); + } + } + /** Update primitive value based on parameter */ + private updatePrimitive(primitive: { parameter: {}[] }): void { + if (primitive.parameter) { + this.primitiveParameter = primitive.parameter; + } else { + this.primitiveParameter = []; + const formArr: AbstractControl[] = this.getControls(); + formArr.forEach((formGp: FormGroup) => { + formGp.controls.primitive_params_name.setValidators([]); + formGp.controls.primitive_params_name.updateValueAndValidity(); + formGp.controls.primitive_params_value.setValidators([]); + formGp.controls.primitive_params_value.updateValueAndValidity(); + }); + } + } + /** Get primivitive actions from vnfd data */ + private getVnfdInfo(vnfdRef: string): void { + this.primitiveList = []; + this.primitiveParameter = []; + this.getFormControl('primitive').setValue(null); + const apiUrl: string = environment.VNFPACKAGES_URL + '?short-name=' + vnfdRef; + this.isLoadingResults = true; + this.restService.getResource(apiUrl) + .subscribe((vnfdInfo: {}) => { + if (vnfdInfo[0]['vnf-configuration']) { + this.primitiveList = vnfdInfo[0]['vnf-configuration']['config-primitive']; + } + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }); + } + /** Used to get the AbstractControl of controlName passed @private */ + private getFormControl(controlName: string): AbstractControl { + return this.primitiveForm.controls[controlName]; + } +} diff --git a/src/app/instances/ns-topology/NSTopologyComponent.html b/src/app/instances/ns-topology/NSTopologyComponent.html new file mode 100644 index 0000000..0a3051c --- /dev/null +++ b/src/app/instances/ns-topology/NSTopologyComponent.html @@ -0,0 +1,173 @@ + + + + + + + + +
+ +
+
+
+
+
+
+
+
+ +
+
+
+ + +
VNFR
+ + +
VL
+ + +
CP
+
+
+
+
+ +
+
+
+
+
+ \ No newline at end of file diff --git a/src/app/instances/ns-topology/NSTopologyComponent.scss b/src/app/instances/ns-topology/NSTopologyComponent.scss new file mode 100644 index 0000000..d750ccc --- /dev/null +++ b/src/app/instances/ns-topology/NSTopologyComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/instances/ns-topology/NSTopologyComponent.ts b/src/app/instances/ns-topology/NSTopologyComponent.ts new file mode 100644 index 0000000..44c6309 --- /dev/null +++ b/src/app/instances/ns-topology/NSTopologyComponent.ts @@ -0,0 +1,573 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file NS Topology Component + */ +/* tslint:disable:no-increment-decrement */ +import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { ERRORDATA } from 'CommonModel'; +import * as d3 from 'd3'; +import { environment } from 'environment'; +import * as HttpStatus from 'http-status-codes'; +import { VNFDCONNECTIONPOINTREF } from 'NSDModel'; +import { COMPOSERNODES, CONNECTIONPOINT, NSD, NSDVLD, NSINFO, NSInstanceDetails, NSINSTANCENODES, VLINFO, VNFRINFO } from 'NSInstanceModel'; +import { GRAPHDETAILS, Tick, TickPath } from 'NSTopologyModel'; +import { RestService } from 'src/services/RestService'; +import { isNullOrUndefined } from 'util'; + +/** + * Creating component + * @Component takes NSTopologyComponent.html as template url + */ +@Component({ + selector: 'app-ns-topology', + templateUrl: './NSTopologyComponent.html', + styleUrls: ['./NSTopologyComponent.scss'], + encapsulation: ViewEncapsulation.None +}) +/** Exporting a class @exports NSTopologyComponent */ +export class NSTopologyComponent { + /** Injector to invoke other services @public */ + public injector: Injector; + /** View child contains graphContainer ref @public */ + @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef; + /** Holds the basic information of NS @public */ + public nsInfo: NSINFO; + /** Contains tranlsate instance @private */ + public translateService: TranslateService; + /** Add the activeclass for the selected @public */ + public activeClass: string = 'active'; + /** Add the fixed class for the freeze @public */ + public fixedClass: string = 'fixed'; + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + /** Assign the forcesimulation active @public */ + public forceSimulationActive: boolean = false; + /** Assign pinned class for the button when freezed @public */ + public classApplied: boolean = false; + /** Contains sidebar open status @public */ + public sideBarOpened: boolean = true; + /** Need to show the NS Details @public */ + public isShowNSDetails: boolean = true; + /** Need to show the VL Details @public */ + public isShowVLetails: boolean = false; + /** Need to show the VNFR Details @public */ + public isShowVNFRDetails: boolean = false; + /** Show right side info of Virtual Link @public */ + public virtualLink: VLINFO; + /** Show right side info of Virtual Link @public */ + public vnfr: VNFRINFO; + + /** Contains lastkeypressed instance @private */ + private lastKeyDown: number = -1; + /** Instance of the rest service @private */ + private restService: RestService; + /** Holds the instance of AuthService class of type AuthService @private */ + private activatedRoute: ActivatedRoute; + /** Holds the NS Id @private */ + private nsIdentifier: string; + /** Contains SVG attributes @private */ + // tslint:disable-next-line:no-any + private svg: any; + /** Contains forced node animations @private */ + // tslint:disable-next-line:no-any + private force: any; + /** Contains path information of the node */ + // tslint:disable-next-line:no-any + private path: any; + /** Contains node network @private */ + // tslint:disable-next-line:no-any + private network: any; + /** Contains node square @private */ + // tslint:disable-next-line:no-any + private square: any; + /** Contains node circle @private */ + // tslint:disable-next-line:no-any + private circle: any; + /** Contains the NS information @private */ + private nsData: NSInstanceDetails; + /** Contains NDS information of a descriptors */ + private nsdData: NSD; + /** Contains node information @private */ + private nodes: NSINSTANCENODES[] = []; + /** Contains links information @private */ + private links: {}[] = []; + /** holds cp count/iteration @private */ + private cpCount: number; + /** VNFD nodes @private */ + private vnfdNodes: {}[] = []; + /** VLD nodes @private */ + private vldNodes: {}[] = []; + /** Connection CP nodes @private */ + private cpNodes: {}[] = []; + /** Set timeout @private */ + private TIMEOUT: number = 2000; + /** Rendered nodes represent vnf @private */ + // tslint:disable-next-line:no-any + private gSquare: any; + /** Rendered nodes represent network @private */ + // tslint:disable-next-line:no-any + private gNetwork: any; + /** Rendered nodes represent network @private */ + // tslint:disable-next-line:no-any + private gCircle: any; + /** Service holds the router information @private */ + private router: Router; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.activatedRoute = this.injector.get(ActivatedRoute); + this.translateService = this.injector.get(TranslateService); + this.router = this.injector.get(Router); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate @public + */ + public ngOnInit(): void { + // tslint:disable-next-line:no-backbone-get-set-outside-model + this.nsIdentifier = this.activatedRoute.snapshot.paramMap.get('id'); + this.generateData(); + } + /** Event to freeze the animation @public */ + public onFreeze(): void { + this.classApplied = !this.classApplied; + const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass); + d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive); + if (alreadyFixedIsActive) { + this.force.stop(); + } + this.forceSimulationActive = alreadyFixedIsActive; + this.nodes.forEach((d: COMPOSERNODES) => { + d.fx = (alreadyFixedIsActive) ? null : d.x; + d.fy = (alreadyFixedIsActive) ? null : d.y; + }); + if (alreadyFixedIsActive) { + this.force.restart(); + } + } + /** Events handles when dragended @public */ + public toggleSidebar(): void { + this.sideBarOpened = !this.sideBarOpened; + this.deselectAllNodes(); + this.showRightSideInfo(true, false, false); + } + /** Get the default Configuration of containers @private */ + private getGraphContainerAttr(): GRAPHDETAILS { + return { + width: 700, + height: 400, + nodeHeight: 50, + nodeWidth: 35, + textX: -35, + textY: 30, + radius: 5, + distance: 50, + strength: -500, + forcex: 2, + forcey: 2, + sourcePaddingYes: 17, + sourcePaddingNo: 12, + targetPaddingYes: 4, + targetPaddingNo: 3, + alphaTarget: 0.3, + imageX: -25, + imageY: -25, + shiftKeyCode: 17 + }; + } + /** Show the right-side information @private */ + private showRightSideInfo(nsDetails: boolean, vlDetails: boolean, vnfrDeails: boolean): void { + this.isShowNSDetails = nsDetails; + this.isShowVLetails = vlDetails; + this.isShowVNFRDetails = vnfrDeails; + } + /** De-select all the selected nodes @private */ + private deselectAllNodes(): void { + this.square.select('image').classed(this.activeClass, false); + this.network.select('image').classed(this.activeClass, false); + this.circle.select('image').classed(this.activeClass, false); + } + /** Prepare all the information for node creation @private */ + private generateData(): void { + this.restService.getResource(environment.NSINSTANCESCONTENT_URL + '/' + this.nsIdentifier).subscribe((nsData: NSInstanceDetails) => { + this.nsData = nsData; + this.nsInfo = { + nsInstanceID: nsData._id, + nsName: nsData.name, + nsOperationalStatus: nsData['operational-status'], + nsConfigStatus: nsData['config-status'], + nsDetailedStatus: nsData['detailed-status'], + nsResourceOrchestrator: nsData['resource-orchestrator'] + }; + if (this.nsData['constituent-vnfr-ref'] !== undefined) { + this.generateVNFRCPNodes(); + } + if (this.nsData.vld !== undefined) { + this.generateVLDNetworkNodes(); + } + setTimeout(() => { + this.pushAllNodes(); + this.generateVNFDCP(); + this.generateVLDCP(); + this.isLoadingResults = false; + this.createNode(this.nodes, this.links); + }, this.TIMEOUT); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) { + this.router.navigateByUrl('404', { skipLocationChange: true }).catch(); + } else { + this.restService.handleError(error, 'get'); + } + }); + } + + /** Fetching all the VNFR Information @private */ + private generateVNFRCPNodes(): void { + this.nsData['constituent-vnfr-ref'].forEach((vnfdrID: string) => { + this.restService.getResource(environment.VNFINSTANCES_URL + '/' + vnfdrID).subscribe((vndfrDetail: NSD) => { + this.nodes.push({ + id: vndfrDetail['vnfd-ref'] + ':' + vndfrDetail['member-vnf-index-ref'], + nodeTypeRef: 'vnfd', + cp: vndfrDetail['connection-point'], + vdur: vndfrDetail.vdur, + vld: vndfrDetail.vld, + nsID: vndfrDetail['nsr-id-ref'], + vnfdID: vndfrDetail['vnfd-id'], + vimID: vndfrDetail['vim-account-id'], + vndfrID: vndfrDetail.id, + ipAddress: vndfrDetail['ip-address'], + memberIndex: vndfrDetail['member-vnf-index-ref'], + vnfdRef: vndfrDetail['vnfd-ref'], + selectorId: 'nsInst-' + vndfrDetail.id + }); + // Fetching all the connection point of VNF & Interface + vndfrDetail['connection-point'].forEach((cp: CONNECTIONPOINT) => { + this.nodes.push({ + id: cp.name + ':' + vndfrDetail['member-vnf-index-ref'], + vndfCPRef: vndfrDetail['vnfd-ref'] + ':' + vndfrDetail['member-vnf-index-ref'], + nodeTypeRef: 'cp', + name: cp.name + }); + }); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + }); + } + + /** Fetching all the VLD/Network Information @private */ + private generateVLDNetworkNodes(): void { + this.nsdData = this.nsData.nsd; + this.nsdData.vld.forEach((ref: NSDVLD) => { + this.nodes.push({ + id: ref.id, + nodeTypeRef: 'vld', + name: ref.name, + type: ref.type, + vnfdCP: ref['vnfd-connection-point-ref'], + vimNetworkName: ref['vim-network-name'], + shortName: ref['short-name'], + selectorId: 'nsInst-' + ref.id + }); + }); + } + + /** Pushing connection points of path/links nodes @private */ + private pushAllNodes(): void { + this.nodes.forEach((nodeList: NSINSTANCENODES) => { + if (nodeList.nodeTypeRef === 'vnfd') { + this.vnfdNodes.push(nodeList); + } else if (nodeList.nodeTypeRef === 'vld') { + this.vldNodes.push(nodeList); + } else if (nodeList.nodeTypeRef === 'cp') { + this.cpNodes.push(nodeList); + } + }); + } + + /** Get CP position based on vndf @private */ + private generateVNFDCP(): void { + this.vnfdNodes.forEach((list: NSINSTANCENODES) => { + const vndfPos: number = this.nodes.map((e: NSINSTANCENODES) => { return e.id; }).indexOf(list.id); + this.cpCount = 0; + this.nodes.forEach((res: NSINSTANCENODES) => { + if (res.nodeTypeRef === 'cp' && res.vndfCPRef === list.id) { + this.links.push({ source: this.nodes[vndfPos], target: this.nodes[this.cpCount] }); + } + this.cpCount++; + }); + }); + } + + /** Get CP position based on vld @private */ + private generateVLDCP(): void { + let vldPos: number = 0; + this.vldNodes.forEach((list: NSINSTANCENODES) => { + if (!isNullOrUndefined(list.vnfdCP)) { + list.vnfdCP.forEach((cpRef: VNFDCONNECTIONPOINTREF) => { + this.cpCount = 0; + this.nodes.forEach((res: NSINSTANCENODES) => { + if (res.nodeTypeRef === 'cp' && res.id === cpRef['vnfd-connection-point-ref'] + ':' + cpRef['member-vnf-index-ref']) { + this.links.push({ source: this.nodes[vldPos], target: this.nodes[this.cpCount] }); + } + this.cpCount++; + }); + }); + vldPos++; + } + }); + } + + /** Node is created and render at D3 region @private */ + private createNode(nodes: NSINSTANCENODES[], links: {}[]): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + d3.selectAll('svg#graphContainer > *').remove(); + d3.select(window).on('keydown', () => { this.keyDown(); }); + d3.select(window).on('keyup', () => { this.keyUp(); }); + this.svg = d3.select('#graphContainer') + .attr('oncontextmenu', 'return false;') + .attr('width', graphContainerAttr.width) + .attr('height', graphContainerAttr.height); + this.force = d3.forceSimulation() + .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength)) + .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance)) + .force('center', d3.forceCenter(graphContainerAttr.width / graphContainerAttr.forcex, + graphContainerAttr.height / graphContainerAttr.forcey)) + .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex)) + .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey)) + .on('tick', () => { this.tick(); }); + // handles to link and node element groups + this.path = this.svg.append('svg:g').selectAll('path'); + this.network = this.svg.append('svg:g').selectAll('network'); + this.square = this.svg.append('svg:g').selectAll('rect'); + this.circle = this.svg.append('svg:g').selectAll('circle'); + this.restart(nodes, links); + } + + /** Update force layout (called automatically each iteration) @private */ + private tick(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + // draw directed edges with proper padding from node centers + this.path.attr('class', 'link').attr('d', (d: Tick) => { + const deltaX: number = d.target.x - d.source.x; + const deltaY: number = d.target.y - d.source.y; + const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY); + const normX: number = deltaX / dist; + const normY: number = deltaY / dist; + const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo; + const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo; + const sourceX: number = d.source.x + (sourcePadding * normX); + const sourceY: number = d.source.y + (sourcePadding * normY); + const targetX: number = d.target.x - (targetPadding * normX); + const targetY: number = d.target.y - (targetPadding * normY); + return `M${sourceX},${sourceY}L${targetX},${targetY}`; + }); + this.network.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`); + this.square.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`); + this.circle.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`); + } + + /** Update graph (called when needed) @private */ + private restart(nodes: NSINSTANCENODES[], links: {}[]): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.path = this.path.data(links); + const vnfdNodes: {}[] = []; const vldNodes: {}[] = []; const cpNodes: {}[] = []; // NB: Nodes are known by id, not by index! + nodes.forEach((nodeList: NSINSTANCENODES) => { + if (nodeList.nodeTypeRef === 'vnfd') { vnfdNodes.push(nodeList); } + else if (nodeList.nodeTypeRef === 'vld') { vldNodes.push(nodeList); } + else if (nodeList.nodeTypeRef === 'cp') { cpNodes.push(nodeList); } + }); + this.square = this.square.data(vnfdNodes, (d: { id: number }) => d.id); + this.network = this.network.data(vldNodes, (d: { id: number }) => d.id); + this.circle = this.circle.data(cpNodes, (d: { id: number }) => d.id); + this.resetAndCreateNodes(); + this.force.nodes(nodes).force('link').links(links); //Set the graph in motion + this.force.alphaTarget(graphContainerAttr.alphaTarget).restart(); + } + + /** Rest and create nodes @private */ + private resetAndCreateNodes(): void { + this.path.exit().remove(); + this.square.exit().remove(); + this.network.exit().remove(); + this.circle.exit().remove(); + // tslint:disable-next-line:no-any + const gPath: any = this.path.enter().append('svg:path').attr('class', 'link'); + this.getgSquare(); + this.getgNetwork(); + this.getgCircle(); + this.square = this.gSquare.merge(this.square); + this.network = this.gNetwork.merge(this.network); + this.path = gPath.merge(this.path); + this.circle = this.gCircle.merge(this.circle); + } + + /** Events handles when Shift Click to perform create cp @private */ + // tslint:disable-next-line: no-any + private singleClick(nodeSelected: any, d: COMPOSERNODES): void { + this.selectNodeExclusive(nodeSelected, d); + } + /** Selected nodes @private */ + // tslint:disable-next-line: no-any + private selectNodeExclusive(nodeSelected: any, d: COMPOSERNODES): void { + const alreadyIsActive: boolean = nodeSelected.select('#' + d.selectorId).classed(this.activeClass); + this.deselectAllNodes(); + nodeSelected.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive); + if (d.nodeTypeRef === 'vld' && !alreadyIsActive) { + this.virtualLink = { + id: d.id, + name: d.name, + type: d.type, + shortName: d.shortName, + vimNetworkName: d.vimNetworkName + }; + this.showRightSideInfo(false, true, false); + } else if (d.nodeTypeRef === 'vnfd' && !alreadyIsActive) { + this.vnfr = { + vimID: d.vimID, + _id: d.vndfrID, + ip: d.ipAddress, + nsrID: d.nsID, + id: d.selectorId, + vnfdRef: d.vnfdRef, + vnfdId: d.vnfdID, + memberIndex: d.memberIndex + }; + this.showRightSideInfo(false, false, true); + } else { + this.showRightSideInfo(true, false, false); + } + } + /** Setting all the square/vnf attributes of nodes @private */ + private getgSquare(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.gSquare = this.square.enter().append('svg:g'); + this.gSquare.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee'); + this.gSquare.append('svg:image') + .style('opacity', 1) + .attr('x', graphContainerAttr.imageX) + .attr('y', graphContainerAttr.imageY) + .attr('id', (d: COMPOSERNODES) => { return d.selectorId; }) + .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight) + .attr('xlink:href', 'assets/images/VNFD.svg') + .on('click', (d: COMPOSERNODES) => { this.singleClick(this.gSquare, d); this.onNodeClickToggleSidebar(); }); + this.gSquare.append('svg:text') + .attr('class', 'node_text') + .attr('y', graphContainerAttr.textY) + .text((d: COMPOSERNODES) => d.id); + } + + /** Settings all the network attributes of nodes @private */ + private getgNetwork(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.gNetwork = this.network.enter().append('svg:g'); + this.gNetwork.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee'); + this.gNetwork.append('svg:image') + .style('opacity', 1) + .attr('x', graphContainerAttr.imageX) + .attr('y', graphContainerAttr.imageY) + .attr('id', (d: COMPOSERNODES) => { return d.selectorId; }) + .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight) + .attr('xlink:href', 'assets/images/VL.svg') + .on('click', (d: COMPOSERNODES) => { this.singleClick(this.gNetwork, d); this.onNodeClickToggleSidebar(); }); + this.gNetwork.append('svg:text') + .attr('class', 'node_text') + .attr('y', graphContainerAttr.textY) + .text((d: COMPOSERNODES) => d.name); + } + + /** Settings all the connection point attributes of nodes @private */ + private getgCircle(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.gCircle = this.circle.enter().append('svg:g'); + this.gCircle.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee'); + this.gCircle.append('svg:image') + .style('opacity', 1) + .attr('x', graphContainerAttr.imageX) + .attr('y', graphContainerAttr.imageY) + .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight) + .attr('xlink:href', 'assets/images/CP.svg'); + this.gCircle.append('svg:text') + .attr('class', 'node_text') + .attr('y', graphContainerAttr.textY) + .text((d: COMPOSERNODES) => d.name); + } + + /** Key press event @private */ + private keyDown(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + if (this.lastKeyDown !== -1) { return; } + this.lastKeyDown = d3.event.keyCode; + if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) { + this.gSquare.call(d3.drag() + .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended) + ); + this.gNetwork.call(d3.drag() + .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended) + ); + this.gCircle.call(d3.drag() + .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended) + ); + this.svg.classed('ctrl', true); + } + } + /** Key realse event @private */ + private keyUp(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.lastKeyDown = -1; + if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) { + this.gSquare.on('.drag', null); + this.gNetwork.on('.drag', null); + this.gCircle.on('.drag', null); + this.svg.classed('ctrl', false); + } + } + /** Events handles when dragstarted @private */ + private dragstarted(d: COMPOSERNODES): void { + d.fx = d.x; + d.fy = d.y; + } + /** Events handles when dragged @private */ + private dragged(d: COMPOSERNODES): void { + d.fx = d.x = d3.event.x; + d.fy = d.y = d3.event.y; + } + /** Events handles when dragended @private */ + private dragended(d: COMPOSERNODES): void { + if (this.forceSimulationActive) { + d.fx = null; + d.fy = null; + } else { + d.fx = d.x; + d.fy = d.y; + this.forceSimulationActive = false; + } + } + /** Events handles when node single click @private */ + private onNodeClickToggleSidebar(): void { + this.sideBarOpened = true; + } +} diff --git a/src/app/instances/pdu-instances/PDUInstancesComponent.html b/src/app/instances/pdu-instances/PDUInstancesComponent.html new file mode 100644 index 0000000..3c18bfa --- /dev/null +++ b/src/app/instances/pdu-instances/PDUInstancesComponent.html @@ -0,0 +1,36 @@ + +
+
{{'PDUINSTANCES' | translate}}
+ + + +
+
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/instances/pdu-instances/PDUInstancesComponent.scss b/src/app/instances/pdu-instances/PDUInstancesComponent.scss new file mode 100644 index 0000000..0ecd95d --- /dev/null +++ b/src/app/instances/pdu-instances/PDUInstancesComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ \ No newline at end of file diff --git a/src/app/instances/pdu-instances/PDUInstancesComponent.ts b/src/app/instances/pdu-instances/PDUInstancesComponent.ts new file mode 100644 index 0000000..20b44df --- /dev/null +++ b/src/app/instances/pdu-instances/PDUInstancesComponent.ts @@ -0,0 +1,197 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file PDU Instance Component + */ +import { Component, Injector, OnDestroy, OnInit } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { AddPDUInstancesComponent } from 'AddPDUInstancesComponent'; +import { ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { LocalDataSource } from 'ng2-smart-table'; +import { PDUInstanceDetails } from 'PDUInstanceModel'; +import { PDUInstancesActionComponent } from 'PDUInstancesActionComponent'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes PDUInstancesComponent.html as template url + */ +@Component({ + templateUrl: './PDUInstancesComponent.html', + styleUrls: ['./PDUInstancesComponent.scss'] +}) +/** Exporting a class @exports PDUInstancesComponent */ +export class PDUInstancesComponent implements OnInit, OnDestroy { + /** Injector to invoke other services @public */ + public injector: Injector; + + /** NS Instance array @public */ + public pduInstanceData: object[] = []; + + /** Datasource instance @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** SelectedRows array @public */ + public selectedRows: object[] = []; + + /** Selected list array @public */ + public selectList: object[] = []; + + /** Instance component are stored in settings @public */ + public settings: {} = {}; + + /** Contains objects for menu settings @public */ + public columnList: {} = {}; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Utilizes rest service for any CRUD operations @private */ + private restService: RestService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.translateService = this.injector.get(TranslateService); + this.modalService = this.injector.get(NgbModal); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.generateTableColumn(); + this.generateTableSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** Generate smart table row title and filters @public */ + public generateTableSettings(): void { + this.settings = { + columns: this.columnList, + actions: { add: false, edit: false, delete: false, position: 'right' }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** Generate smart table row title and filters @public */ + public generateTableColumn(): void { + this.columnList = { + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '25%' }, + name: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' }, + type: { title: this.translateService.instant('TYPE'), width: '15%' }, + usageState: { title: this.translateService.instant('USAGESTATE'), width: '15%' }, + CreatedAt: { title: this.translateService.instant('CREATEDAT'), width: '15%' }, + Actions: { + name: 'Action', width: '10%', filter: false, sort: false, type: 'custom', + title: this.translateService.instant('ACTIONS'), + valuePrepareFunction: (cell: PDUInstanceDetails, row: PDUInstanceDetails): PDUInstanceDetails => row, + renderComponent: PDUInstancesActionComponent + } + }; + } + + /** generateData initiate the ns-instance list @public */ + public generateData(): void { + this.pduInstanceData = []; + this.isLoadingResults = true; + this.restService.getResource(environment.PDUINSTANCE_URL).subscribe((pduInstancesData: PDUInstanceDetails[]) => { + pduInstancesData.forEach((pduInstanceData: PDUInstanceDetails) => { + const pduDataObj: {} = { + name: pduInstanceData.name, + identifier: pduInstanceData._id, + type: pduInstanceData.type, + usageState: pduInstanceData._admin.usageState, + CreatedAt: this.sharedService.convertEpochTime(Number(pduInstanceData._admin.created)) + }; + this.pduInstanceData.push(pduDataObj); + }); + if (this.pduInstanceData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.pduInstanceData).then((data: {}) => { + this.isLoadingResults = false; + }).catch(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** smart table listing manipulation @public */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** smart table listing manipulation @public */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'pdu-instances' }); + this.dataService.changeMessage(event.data); + } + + /** Add PDU Instance modal using modalservice @public */ + public addPDUInstanceModal(): void { + const modalRef: NgbModalRef = this.modalService.open(AddPDUInstancesComponent, { backdrop: 'static' }); + modalRef.componentInstance.title = this.translateService.instant('PAGE.PDUINSTANCE.NEWPDUINSTANCE'); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.generateData(); + } + }).catch(); + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } +} diff --git a/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.html b/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.html new file mode 100644 index 0000000..f785426 --- /dev/null +++ b/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.html @@ -0,0 +1,95 @@ + + +
+ + +
+ \ No newline at end of file diff --git a/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.scss b/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.scss new file mode 100644 index 0000000..950bb39 --- /dev/null +++ b/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.scss @@ -0,0 +1,34 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +@import '../../../../assets/scss/mixins/mixin'; +@import '../../../../assets/scss/variable'; + +.head { + @include padding-value(10, 10, 10, 10); + line-height: 2em; + .btn-params { + @include border(all, 1, solid, $white); + } +} + +.remove-params { + display: flex; + align-items: center; + font-size: 20px; + cursor: pointer; +} \ No newline at end of file diff --git a/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.ts b/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.ts new file mode 100644 index 0000000..0dcbb60 --- /dev/null +++ b/src/app/instances/pdu-instances/add-pdu-instances/AddPDUInstancesComponent.ts @@ -0,0 +1,197 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file ADD PDU Instances Component + */ +import { Component, Injector, Input, OnInit, Output } from '@angular/core'; +import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { PDUInstanceDetails } from 'PDUInstanceModel'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { VimAccountDetails } from 'VimAccountModel'; + +/** + * Creating component + * @Component takes AddPDUInstancesComponent.html as template url + */ +@Component({ + templateUrl: './AddPDUInstancesComponent.html', + styleUrls: ['./AddPDUInstancesComponent.scss'] +}) +/** Exporting a class @exports AddPDUInstancesComponent */ +export class AddPDUInstancesComponent implements OnInit { + /** Form valid on submit trigger @public */ + public submitted: boolean = false; + + /** To inject services @public */ + public injector: Injector; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** FormGroup instance added to the form @ html @public */ + public pduInstancesForm: FormGroup; + + /** Primitive params array @public */ + public pduInterfaces: FormArray; + + /** Variable set for twoway binding @public */ + public pduInstanceId: string; + + /** Set mgmt field to empty on load @public */ + public selectedMgmt: string; + + /** Set vim field to empty on load @public */ + public selectedVIM: string; + + /** Contains boolean value as select options for mgmt @public */ + public mgmtState: {}[] = [{ name: 'True', value: true }, { name: 'False', value: false }]; + + /** Input contains Modal dialog component Instance @private */ + @Input() public title: string; + + /** Contains all the vim accounts list @public */ + public vimAccountSelect: VimAccountDetails; + + /** Check the loading results @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Utilizes rest service for any CRUD operations @private */ + private restService: RestService; + + /** packages data service collections @private */ + private dataService: DataService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.translateService = this.injector.get(TranslateService); + this.notifierService = this.injector.get(NotifierService); + this.sharedService = this.injector.get(SharedService); + this.activeModal = this.injector.get(NgbActiveModal); + this.formBuilder = this.injector.get(FormBuilder); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + /** Setting up initial value for NSD */ + this.dataService.currentMessage.subscribe((event: PDUInstanceDetails) => { + if (event.identifier !== undefined || event.identifier !== '' || event.identifier !== null) { + this.pduInstanceId = event.identifier; + } + }); + this.generateVIMAccounts(); + this.initializeForm(); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.pduInstancesForm.controls; } + + /** initialize Forms @public */ + public initializeForm(): void { + this.pduInstancesForm = this.formBuilder.group({ + name: ['', [Validators.required]], + type: ['', [Validators.required]], + vim_accounts: ['', [Validators.required]], + interfaces: this.formBuilder.array([this.interfacesBuilder()]) + }); + } + + /** Generate interfaces fields @public */ + public interfacesBuilder(): FormGroup { + return this.formBuilder.group({ + name: ['', [Validators.required]], + 'ip-address': ['', [Validators.required, Validators.pattern(this.sharedService.REGX_IP_PATTERN)]], + mgmt: ['', [Validators.required]], + 'vim-network-name': ['', [Validators.required]] + }); + } + + /** Handle FormArray Controls @public */ + public getControls(): AbstractControl[] { + // tslint:disable-next-line:no-backbone-get-set-outside-model + return (this.pduInstancesForm.get('interfaces') as FormArray).controls; + } + + /** Push all primitive params on user's action @public */ + public createInterfaces(): void { + // tslint:disable-next-line:no-backbone-get-set-outside-model + this.pduInterfaces = this.pduInstancesForm.get('interfaces') as FormArray; + this.pduInterfaces.push(this.interfacesBuilder()); + } + + /** Remove interfaces on user's action @public */ + public removeInterfaces(index: number): void { + this.pduInterfaces.removeAt(index); + } + + /** Execute New PDU Instances @public */ + public createPDUInstances(): void { + this.submitted = true; + this.sharedService.cleanForm(this.pduInstancesForm); + if (this.pduInstancesForm.invalid) { return; } // Proceed, onces form is valid + this.isLoadingResults = true; + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + const apiURLHeader: APIURLHEADER = { + url: environment.PDUINSTANCE_URL + }; + this.restService.postResource(apiURLHeader, this.pduInstancesForm.value).subscribe((result: {}) => { + this.activeModal.close(modalData); + this.notifierService.notify('success', this.translateService.instant('PAGE.PDUINSTANCE.CREATEDSUCCESSFULLY')); + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + this.isLoadingResults = false; + }); + } + + /** Generate vim accounts list @public */ + public generateVIMAccounts(): void { + this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimData: VimAccountDetails) => { + this.vimAccountSelect = vimData; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } +} diff --git a/src/app/instances/vnf-instances/VNFInstancesComponent.html b/src/app/instances/vnf-instances/VNFInstancesComponent.html new file mode 100644 index 0000000..e36cc3d --- /dev/null +++ b/src/app/instances/vnf-instances/VNFInstancesComponent.html @@ -0,0 +1,29 @@ + +
+
{{'VNFINSTANCES' | translate}}
+
+
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/instances/vnf-instances/VNFInstancesComponent.scss b/src/app/instances/vnf-instances/VNFInstancesComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/instances/vnf-instances/VNFInstancesComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/instances/vnf-instances/VNFInstancesComponent.ts b/src/app/instances/vnf-instances/VNFInstancesComponent.ts new file mode 100644 index 0000000..d829ee2 --- /dev/null +++ b/src/app/instances/vnf-instances/VNFInstancesComponent.ts @@ -0,0 +1,187 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file NVF Instance Component + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { ERRORDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { LocalDataSource } from 'ng2-smart-table'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; +import { VNFInstanceData, VNFInstanceDetails } from 'VNFInstanceModel'; +import { VNFInstancesActionComponent } from 'VNFInstancesActionComponent'; +import { VNFLinkComponent } from 'VNFLinkComponent'; + +/** + * Creating component + * @Component takes VNFInstancesComponent.html as template url + */ +@Component({ + templateUrl: './VNFInstancesComponent.html', + styleUrls: ['./VNFInstancesComponent.scss'] +}) +/** Exporting a class @exports VNFInstancesComponent */ +export class VNFInstancesComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** smart table data service collections @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** Instance component are stored in settings @public */ + public settings: {} = {}; + + /** Contains objects for menu settings @public */ + public columnList: {} = {}; + + /** vnf instance array @public */ + public vnfInstanceData: {}[] = []; + + /** selected rows array @public */ + public selectedRows: string[] = []; + + /** selected list array @public */ + public selectList: string[] = []; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** Utilizes rest service for any CRUD operations @private */ + private restService: RestService; + + /** packages data service collections @private */ + private dataService: DataService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.translateService = this.injector.get(TranslateService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.generateTableColumn(); + this.generateTableSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** Generate smart table row title and filters @public */ + public generateTableSettings(): void { + this.settings = { + actions: { add: false, edit: false, delete: false, position: 'right' }, + columns: this.columnList, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** Generate smart table row title and filters @public */ + public generateTableColumn(): void { + this.columnList = { + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '25%', sortDirection: 'asc' }, + VNFD: { + title: this.translateService.instant('VNFD'), width: '20%', type: 'custom', + valuePrepareFunction: (cell: VNFInstanceData, row: VNFInstanceData): VNFInstanceData => row, + renderComponent: VNFLinkComponent + }, + MemberIndex: { title: this.translateService.instant('MEMBERINDEX'), width: '15%' }, + NS: { title: this.translateService.instant('NS'), width: '20%' }, + CreatedAt: { title: this.translateService.instant('CREATEDAT'), width: '15%' }, + Actions: { + name: 'Action', width: '5%', filter: false, sort: false, type: 'custom', + title: this.translateService.instant('ACTIONS'), + valuePrepareFunction: (cell: VNFInstanceData, row: VNFInstanceData): VNFInstanceData => row, + renderComponent: VNFInstancesActionComponent + } + }; + } + + /** generateData initiate the vnf-instance list */ + public generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.VNFINSTANCES_URL).subscribe((vnfInstancesData: VNFInstanceDetails[]) => { + this.vnfInstanceData = []; + vnfInstancesData.forEach((vnfInstanceData: VNFInstanceDetails) => { + const vnfDataObj: {} = + { + VNFD: vnfInstanceData['vnfd-ref'], + identifier: vnfInstanceData._id, + MemberIndex: vnfInstanceData['member-vnf-index-ref'], + NS: vnfInstanceData['nsr-id-ref'], + VNFID: vnfInstanceData['vnfd-id'], + CreatedAt: this.sharedService.convertEpochTime(Number(vnfInstanceData['created-time'])) + }; + this.vnfInstanceData.push(vnfDataObj); + }); + if (this.vnfInstanceData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.vnfInstanceData).then((data: {}) => { + this.isLoadingResults = false; + }).catch(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** smart table listing manipulation @public */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** smart table listing manipulation @public */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'vnf-instance' }); + this.dataService.changeMessage(event.data); + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } +} diff --git a/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.html b/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.html new file mode 100644 index 0000000..450e8a3 --- /dev/null +++ b/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.html @@ -0,0 +1,18 @@ + +{{value.VNFD}} diff --git a/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.scss b/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.ts b/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.ts new file mode 100644 index 0000000..15ea6ff --- /dev/null +++ b/src/app/instances/vnf-instances/vnf-link/VNFLinkComponent.ts @@ -0,0 +1,46 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file VNFD Link Component. + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { VNFInstanceData } from 'VNFInstanceModel'; +/** + * Creating component + * @Component takes VNFLinkComponent.html as template url + */ +@Component({ + selector: 'app-vnf-link', + templateUrl: './VNFLinkComponent.html', + styleUrls: ['./VNFLinkComponent.scss'] +}) +/** Exporting a class @exports VnfLinkComponent */ +export class VNFLinkComponent implements OnInit { + /** Invoke service injectors @public */ + public injector: Injector; + /** To get the value from the VNFInstance via valuePrepareFunction default Property of ng-smarttable @public */ + public value: VNFInstanceData; + constructor(injector: Injector) { + this.injector = injector; + } + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + //empty + } + +} diff --git a/src/app/k8s/K8sComponent.html b/src/app/k8s/K8sComponent.html new file mode 100644 index 0000000..3f96ff8 --- /dev/null +++ b/src/app/k8s/K8sComponent.html @@ -0,0 +1,18 @@ + + \ No newline at end of file diff --git a/src/app/k8s/K8sComponent.scss b/src/app/k8s/K8sComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/k8s/K8sComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/k8s/K8sComponent.ts b/src/app/k8s/K8sComponent.ts new file mode 100644 index 0000000..5427a8f --- /dev/null +++ b/src/app/k8s/K8sComponent.ts @@ -0,0 +1,56 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file k8s.ts. + */ +import { Component, Injector } from '@angular/core'; +import { Router, RouterEvent } from '@angular/router'; +/** + * Creating Component + * @Component takes K8sComponent.html as template url + */ +@Component({ + selector: 'app-k8s', + templateUrl: './K8sComponent.html', + styleUrls: ['./K8sComponent.scss'] +}) +/** Exporting a class @exports K8sComponent */ +export class K8sComponent { + /** Invoke service injectors @public */ + public injector: Injector; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + /** creates k8s component */ + constructor(injector: Injector) { + this.injector = injector; + this.router = this.injector.get(Router); + this.router.events.subscribe((event: RouterEvent) => { + this.redirectToList(event.url); + }); + } + + /** Return to list NS Package List */ + public redirectToList(getURL: string): void { + if (getURL === '/k8s') { + this.router.navigate(['/k8s/cluster']).catch(); + } + } + +} diff --git a/src/app/k8s/K8sModule.ts b/src/app/k8s/K8sModule.ts new file mode 100644 index 0000000..51f27b2 --- /dev/null +++ b/src/app/k8s/K8sModule.ts @@ -0,0 +1,106 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file K8s Module. + */ +import { CommonModule } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterModule, Routes } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { TranslateModule } from '@ngx-translate/core'; +import { DataService } from 'DataService'; +import { K8sActionComponent } from 'K8sActionComponent'; +import { K8sAddClusterComponent } from 'K8sAddClusterComponent'; +import { K8sAddRepoComponent } from 'K8sAddRepoComponent'; +import { K8sClusterComponent } from 'K8sClusterComponent'; +import { K8sComponent } from 'K8sComponent'; +import { K8sRepositoryComponent } from 'K8sRepositoryComponent'; +import { LoaderModule } from 'LoaderModule'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { PagePerRowModule } from 'PagePerRowModule'; +import { PageReloadModule } from 'PageReloadModule'; + +/** To halndle project information */ +const projectInfo: {} = { title: '{project}', url: '/' }; + +/** + * configures routers + */ +const routes: Routes = [ + { + path: '', + component: K8sComponent, + children: [ + { + path: 'cluster', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'PAGE.K8S.MENUK8SCLUSTER', url: null }] + }, + component: K8sClusterComponent + }, + { + path: 'repo', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'PAGE.K8S.MENUK8SREPO', url: null }] + }, + component: K8sRepositoryComponent + } + ] + } +]; +/** + * Creating @NgModule component for Modules + */ +@NgModule({ + imports: [ + ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), + FormsModule, + CommonModule, + HttpClientModule, + NgSelectModule, + Ng2SmartTableModule, + TranslateModule, + RouterModule.forChild(routes), + NgbModule, + PagePerRowModule, + LoaderModule, + PageReloadModule + ], + declarations: [ + K8sComponent, + K8sClusterComponent, + K8sRepositoryComponent, + K8sActionComponent, + K8sAddClusterComponent, + K8sAddRepoComponent + ], + providers: [DataService], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + entryComponents: [K8sActionComponent, K8sAddClusterComponent, K8sAddRepoComponent] +}) +/** Exporting a class @exports K8sModule */ +export class K8sModule { + /** Variables declared to avoid state-less class */ + private k8sModule: string; +} diff --git a/src/app/k8s/k8s-action/K8sActionComponent.html b/src/app/k8s/k8s-action/K8sActionComponent.html new file mode 100644 index 0000000..c35bb19 --- /dev/null +++ b/src/app/k8s/k8s-action/K8sActionComponent.html @@ -0,0 +1,27 @@ + +
+ + +
\ No newline at end of file diff --git a/src/app/k8s/k8s-action/K8sActionComponent.scss b/src/app/k8s/k8s-action/K8sActionComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/k8s/k8s-action/K8sActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/k8s/k8s-action/K8sActionComponent.ts b/src/app/k8s/k8s-action/K8sActionComponent.ts new file mode 100644 index 0000000..a524277 --- /dev/null +++ b/src/app/k8s/k8s-action/K8sActionComponent.ts @@ -0,0 +1,103 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file K8 Action Component + */ +import { Component, Injector } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { K8SCLUSTERDATADISPLAY, K8SREPODATADISPLAY } from 'K8sModel'; +import { SharedService } from 'SharedService'; +import { ShowInfoComponent } from 'ShowInfoComponent'; +/** + * Creating component + * @Component takes K8sActionComponent.html as template url + */ +@Component({ + selector: 'app-k8s-action', + templateUrl: './K8sActionComponent.html', + styleUrls: ['./K8sActionComponent.scss'] +}) +/** Exporting a class @exports K8sActionComponent */ +export class K8sActionComponent{ + /** To inject services @public */ + public injector: Injector; + + /** To get the value from the Users action via valuePrepareFunction default Property of ng-smarttable @public */ + public value: K8SCLUSTERDATADISPLAY | K8SREPODATADISPLAY; + + /** handle translate @public */ + public translateService: TranslateService; + + /** Contains K8s Type @private */ + public getK8sType: string; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Contains instance ID @private */ + private instanceID: string; + + constructor(injector: Injector) { + this.injector = injector; + this.modalService = this.injector.get(NgbModal); + this.sharedService = this.injector.get(SharedService); + this.translateService = this.injector.get(TranslateService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.instanceID = this.value.identifier; + this.getK8sType = this.value.pageType; + } + + /** Delete User Account @public */ + public deleteK8s(pageType: string): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** Shows information using modalservice @public */ + public infoK8s(pageType: string): void { + let pageName: string = ''; + let title: string = ''; + if (pageType === 'repo') { + pageName = 'k8s-repo'; + title = 'PAGE.K8S.K8SREPODETAILS'; + } else { + pageName = 'k8s-cluster'; + title = 'PAGE.K8S.K8SCLUSTERDETAILS'; + } + this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = { + id: this.instanceID, + page: pageName, + titleName: title + }; + } +} diff --git a/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.html b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.html new file mode 100644 index 0000000..84f2150 --- /dev/null +++ b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.html @@ -0,0 +1,92 @@ + +
+ + + +
+ \ No newline at end of file diff --git a/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.scss b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.ts b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.ts new file mode 100644 index 0000000..0295b35 --- /dev/null +++ b/src/app/k8s/k8s-add-cluster/K8sAddClusterComponent.ts @@ -0,0 +1,233 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file K8sAddClusterComponent.ts. + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { environment } from 'environment'; +import * as jsyaml from 'js-yaml'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; +import { VimAccountDetails } from 'VimAccountModel'; +/** + * Creating Component + * @Component takes K8sAddClusterComponent.html as template url + */ +@Component({ + selector: 'app-k8s-add-cluster', + templateUrl: './K8sAddClusterComponent.html', + styleUrls: ['./K8sAddClusterComponent.scss'] +}) +/** Exporting a class @exports K8sAddClusterComponent */ +export class K8sAddClusterComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** FormGroup instance added to the form @ html @public */ + public k8sclusterForm: FormGroup; + + /** Contains all vim account collections */ + public vimAccountSelect: VimAccountDetails; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Variable set for twoway bindng @public */ + public vimAccountId: string; + + /** Form submission Add */ + public submitted: boolean = false; + + /** Check the loading results @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Element ref for fileInputNets @public */ + @ViewChild('fileInputNets', { static: true }) public fileInputNets: ElementRef; + + /** Element ref for fileInputNetsLabel @public */ + @ViewChild('fileInputNetsLabel', { static: true }) public fileInputNetsLabel: ElementRef; + + /** Element ref for fileInputCredentials @public */ + @ViewChild('fileInputCredentials', { static: true }) public fileInputCredentials: ElementRef; + + /** Element ref for fileInputCredentialsLabel @public */ + @ViewChild('fileInputCredentialsLabel', { static: true }) public fileInputCredentialsLabel: ElementRef; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Utilizes rest service for any CRUD operations @private */ + private restService: RestService; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + this.formBuilder = this.injector.get(FormBuilder); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + } + + public ngOnInit(): void { + /** On Initializing call the methods */ + this.k8sclusterFormAction(); + this.getDetailsvimAccount(); + this.headers = new HttpHeaders({ + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + } + + /** On modal initializing forms @public */ + public k8sclusterFormAction(): void { + this.k8sclusterForm = this.formBuilder.group({ + name: ['', [Validators.required]], + k8s_version: ['', [Validators.required]], + vim_account: [null, [Validators.required]], + description: ['', [Validators.required]], + nets: ['', [Validators.required]], + credentials: ['', [Validators.required]] + }); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.k8sclusterForm.controls; } + + /** Call the vimAccount details in the selection options @public */ + public getDetailsvimAccount(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimAccounts: VimAccountDetails) => { + this.vimAccountSelect = vimAccounts; + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** On modal submit k8sAddClusterSubmit will called @public */ + public k8sAddClusterSubmit(): void { + this.submitted = true; + this.sharedService.cleanForm(this.k8sclusterForm); + if (this.k8sclusterForm.invalid) { + return; + } + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + const apiURLHeader: APIURLHEADER = { + url: environment.K8SCLUSTER_URL, + httpOptions: { headers: this.headers } + }; + const validJSONCredentails: boolean = this.sharedService.checkJson(this.k8sclusterForm.value.credentials); + if (validJSONCredentails) { + this.k8sclusterForm.value.credentials = jsyaml.load(this.k8sclusterForm.value.credentials.toString(), { json: true }); + } else { + this.notifierService.notify('error', this.translateService.instant('INVALIDCONFIG')); + return; + } + const validJSONNets: boolean = this.sharedService.checkJson(this.k8sclusterForm.value.nets); + if (validJSONNets) { + this.k8sclusterForm.value.nets = jsyaml.load(this.k8sclusterForm.value.nets.toString(), { json: true }); + } else { + this.notifierService.notify('error', this.translateService.instant('INVALIDCONFIG')); + return; + } + this.isLoadingResults = true; + this.restService.postResource(apiURLHeader, this.k8sclusterForm.value).subscribe((result: {}) => { + this.activeModal.close(modalData); + this.isLoadingResults = false; + this.notifierService.notify('success', this.k8sclusterForm.value.name + + this.translateService.instant('PAGE.K8S.CREATEDSUCCESSFULLY')); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + this.isLoadingResults = false; + }); + } + + /** Nets file process @private */ + public netsFile(files: FileList): void { + if (files && files.length === 1) { + this.sharedService.getFileString(files, 'json').then((fileContent: string): void => { + const getNetsJson: string = jsyaml.load(fileContent, { json: true }); + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.k8sclusterForm.get('nets').setValue(JSON.stringify(getNetsJson)); + }).catch((err: string): void => { + if (err === 'typeError') { + this.notifierService.notify('error', this.translateService.instant('JSONFILETYPEERRROR')); + } else { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + this.fileInputNetsLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE'); + this.fileInputNets.nativeElement.value = null; + }); + } else if (files && files.length > 1) { + this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION')); + } + this.fileInputNetsLabel.nativeElement.innerText = files[0].name; + this.fileInputNets.nativeElement.value = null; + } + + /** credentials file process @private */ + public credentialsFile(files: FileList): void { + if (files && files.length === 1) { + this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => { + const getCredentialsJson: string = jsyaml.load(fileContent, { json: true }); + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.k8sclusterForm.get('credentials').setValue(JSON.stringify(getCredentialsJson)); + }).catch((err: string): void => { + if (err === 'typeError') { + this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR')); + } else { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + this.fileInputCredentialsLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE'); + this.fileInputCredentials.nativeElement.value = null; + }); + } else if (files && files.length > 1) { + this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION')); + } + this.fileInputCredentialsLabel.nativeElement.innerText = files[0].name; + this.fileInputCredentials.nativeElement.value = null; + } + +} diff --git a/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.html b/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.html new file mode 100644 index 0000000..8caec5e --- /dev/null +++ b/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.html @@ -0,0 +1,68 @@ + +
+ + + +
+ \ No newline at end of file diff --git a/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.scss b/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.scss new file mode 100644 index 0000000..fdec4ed --- /dev/null +++ b/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ diff --git a/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.ts b/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.ts new file mode 100644 index 0000000..c8b96bb --- /dev/null +++ b/src/app/k8s/k8s-add-repo/K8sAddRepoComponent.ts @@ -0,0 +1,134 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file k8sAddRepoComponent.ts. + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { environment } from 'environment'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +/** + * Creating Component + * @Component takes K8sAddRepoComponent.html as template url + */ +@Component({ + selector: 'app-k8s-add-repo', + templateUrl: './K8sAddRepoComponent.html', + styleUrls: ['./K8sAddRepoComponent.scss'] +}) +/** Exporting a class @exports K8sAddRepoComponent */ +export class K8sAddRepoComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** FormGroup instance added to the form @ html @public */ + public k8srepoForm: FormGroup; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Form submission Add */ + public submitted: boolean = false; + + /** Supported Vim type for the dropdown */ + public repoTypeSelect: {}[]; + + /** Check the loading results @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Utilizes rest service for any CRUD operations @private */ + private restService: RestService; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + this.formBuilder = this.injector.get(FormBuilder); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + } + + public ngOnInit(): void { + this.repoTypeSelect = [ + { id: 'helm-chart', name: 'Helm Chart' }, + { id: 'juju-bundle', name: 'Juju Bundle' } + ]; + /** On Initializing call the methods */ + this.k8srepoFormAction(); + } + + /** On modal initializing forms @public */ + public k8srepoFormAction(): void { + this.k8srepoForm = this.formBuilder.group({ + name: ['', [Validators.required]], + type: [null, [Validators.required]], + url: ['', [Validators.required, Validators.pattern(this.sharedService.REGX_URL_PATTERN)]], + description: ['', [Validators.required]] + }); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.k8srepoForm.controls; } + + /** On modal submit k8sAddRepoSubmit will called @public */ + public k8sAddRepoSubmit(): void { + this.submitted = true; + this.sharedService.cleanForm(this.k8srepoForm); + if (this.k8srepoForm.invalid) { + return; + } + this.isLoadingResults = true; + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + const apiURLHeader: APIURLHEADER = { + url: environment.K8REPOS_URL + }; + this.restService.postResource(apiURLHeader, this.k8srepoForm.value).subscribe((result: {}) => { + this.activeModal.close(modalData); + this.notifierService.notify('success', this.k8srepoForm.value.name + + this.translateService.instant('PAGE.K8S.CREATEDSUCCESSFULLY')); + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'post'); + }); + } + +} diff --git a/src/app/k8s/k8scluster/K8sClusterComponent.html b/src/app/k8s/k8scluster/K8sClusterComponent.html new file mode 100644 index 0000000..ebed3e4 --- /dev/null +++ b/src/app/k8s/k8scluster/K8sClusterComponent.html @@ -0,0 +1,42 @@ + +
+
{{'PAGE.K8S.REGISTERK8CLUSTER' | translate}}
+ + + +
+
+
+ +
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/k8s/k8scluster/K8sClusterComponent.scss b/src/app/k8s/k8scluster/K8sClusterComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/k8s/k8scluster/K8sClusterComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/k8s/k8scluster/K8sClusterComponent.ts b/src/app/k8s/k8scluster/K8sClusterComponent.ts new file mode 100644 index 0000000..7ab7583 --- /dev/null +++ b/src/app/k8s/k8scluster/K8sClusterComponent.ts @@ -0,0 +1,236 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file k8sclustercomponent.ts. + */ +import { Component, Injector, OnDestroy, OnInit } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { CONFIGCONSTANT, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { K8sActionComponent } from 'K8sActionComponent'; +import { K8sAddClusterComponent } from 'K8sAddClusterComponent'; +import { K8SCLUSTERDATA, K8SCLUSTERDATADISPLAY } from 'K8sModel'; +import { LocalDataSource } from 'ng2-smart-table'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; +/** + * Creating Component + * @Component takes K8sClusterComponent.html as template url + */ +@Component({ + selector: 'app-k8scluster', + templateUrl: './K8sClusterComponent.html', + styleUrls: ['./K8sClusterComponent.scss'] +}) +/** Exporting a class @exports K8sClusterComponent */ +export class K8sClusterComponent implements OnInit, OnDestroy { + /** To inject services @public */ + public injector: Injector; + + /** handle translate @public */ + public translateService: TranslateService; + + /** Data of smarttable populate through LocalDataSource @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** Columns list of the smart table @public */ + public columnList: object = {}; + + /** Settings for smarttable to populate the table with columns @public */ + public settings: object = {}; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** operational State init data @public */ + public operationalStateFirstStep: string = CONFIGCONSTANT.k8OperationalStateFirstStep; + + /** operational State running data @public */ + public operationalStateSecondStep: string = CONFIGCONSTANT.k8OperationalStateStateSecondStep; + + /** operational State failed data @public */ + public operationalStateThirdStep: string = CONFIGCONSTANT.k8OperationalStateThirdStep; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Formation of appropriate Data for LocalDatasource @private */ + private k8sClusterData: {}[] = []; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.translateService = this.injector.get(TranslateService); + this.modalService = this.injector.get(NgbModal); + } + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.generateColumns(); + this.generateSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** smart table Header Colums @public */ + public generateColumns(): void { + this.columnList = { + name: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' }, + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' }, + version: { title: this.translateService.instant('K8VERSION'), width: '10%' }, + operationalState: { + title: this.translateService.instant('OPERATIONALSTATE'), width: '15%', type: 'html', + filter: { + type: 'list', + config: { + selectText: 'Select', + list: [ + { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep }, + { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep }, + { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep } + ] + } + }, + valuePrepareFunction: (cell: K8SCLUSTERDATADISPLAY, row: K8SCLUSTERDATADISPLAY): string => { + if (row.operationalState === this.operationalStateFirstStep) { + return ` + + `; + } else if (row.operationalState === this.operationalStateSecondStep) { + return ` + + `; + } else if (row.operationalState === this.operationalStateThirdStep) { + return ` + + `; + } else { + return `${row.operationalState}`; + } + } + }, + created: { title: this.translateService.instant('CREATED'), width: '15%' }, + modified: { title: this.translateService.instant('MODIFIED'), width: '15%' }, + Actions: { + name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom', + valuePrepareFunction: (cell: K8SCLUSTERDATADISPLAY, row: K8SCLUSTERDATADISPLAY): K8SCLUSTERDATADISPLAY => row, + renderComponent: K8sActionComponent + } + }; + } + + /** smart table Data Settings @public */ + public generateSettings(): void { + this.settings = { + columns: this.columnList, + actions: { add: false, edit: false, delete: false, position: 'right' }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** smart table listing manipulation @public */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** smart table listing manipulation @public */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'k8-cluster' }); + this.dataService.changeMessage(event.data); + } + + /** Compose new K8s Cluster Accounts @public */ + public addK8sCluster(): void { + const modalRef: NgbModalRef = this.modalService.open(K8sAddClusterComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } + + /** Generate nsData object from loop and return for the datasource @public */ + public generateK8sclusterData(k8sClusterdata: K8SCLUSTERDATA): K8SCLUSTERDATADISPLAY { + return { + name: k8sClusterdata.name, + identifier: k8sClusterdata._id, + operationalState: k8sClusterdata._admin.operationalState, + version: k8sClusterdata.k8s_version, + created: this.sharedService.convertEpochTime(Number(k8sClusterdata._admin.created)), + modified: this.sharedService.convertEpochTime(Number(k8sClusterdata._admin.modified)), + pageType: 'cluster' + }; + } + + /** Fetching the data from server to Load in the smarttable @protected */ + protected generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.K8SCLUSTER_URL).subscribe((k8sClusterDatas: K8SCLUSTERDATA[]) => { + this.k8sClusterData = []; + k8sClusterDatas.forEach((k8sClusterdata: K8SCLUSTERDATA) => { + const k8sClusterDataObj: K8SCLUSTERDATADISPLAY = this.generateK8sclusterData(k8sClusterdata); + this.k8sClusterData.push(k8sClusterDataObj); + }); + if (this.k8sClusterData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.k8sClusterData).then((data: boolean) => { + this.isLoadingResults = false; + }).catch(() => { + this.isLoadingResults = false; + }); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + +} diff --git a/src/app/k8s/k8srepository/K8sRepositoryComponent.html b/src/app/k8s/k8srepository/K8sRepositoryComponent.html new file mode 100644 index 0000000..e546356 --- /dev/null +++ b/src/app/k8s/k8srepository/K8sRepositoryComponent.html @@ -0,0 +1,35 @@ + +
+
{{'PAGE.K8S.REGISTERK8REPO' | translate}}
+ + + +
+
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/k8s/k8srepository/K8sRepositoryComponent.scss b/src/app/k8s/k8srepository/K8sRepositoryComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/k8s/k8srepository/K8sRepositoryComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/k8s/k8srepository/K8sRepositoryComponent.ts b/src/app/k8s/k8srepository/K8sRepositoryComponent.ts new file mode 100644 index 0000000..1b6c9f7 --- /dev/null +++ b/src/app/k8s/k8srepository/K8sRepositoryComponent.ts @@ -0,0 +1,198 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file K8sRepositoryComponent.ts. + */ +import { Component, Injector, OnDestroy, OnInit } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { K8sActionComponent } from 'K8sActionComponent'; +import { K8sAddRepoComponent } from 'K8sAddRepoComponent'; +import { K8SREPODATA, K8SREPODATADISPLAY } from 'K8sModel'; +import { LocalDataSource } from 'ng2-smart-table'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; +/** + * Creating Component + * @Component takes K8sRepositoryComponent.html as template url + */ +@Component({ + selector: 'app-k8srepository', + templateUrl: './K8sRepositoryComponent.html', + styleUrls: ['./K8sRepositoryComponent.scss'] +}) +/** Exporting a class @exports K8sRepositoryComponent */ +export class K8sRepositoryComponent implements OnInit, OnDestroy { + /** To inject services @public */ + public injector: Injector; + + /** handle translate @public */ + public translateService: TranslateService; + + /** Data of smarttable populate through LocalDataSource @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** Columns list of the smart table @public */ + public columnList: object = {}; + + /** Settings for smarttable to populate the table with columns @public */ + public settings: object = {}; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Formation of appropriate Data for LocalDatasource @private */ + private k8sRepoData: {}[] = []; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.translateService = this.injector.get(TranslateService); + this.modalService = this.injector.get(NgbModal); + } + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.generateColumns(); + this.generateSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** smart table Header Colums @public */ + public generateColumns(): void { + this.columnList = { + name: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' }, + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' }, + url: { title: this.translateService.instant('URL'), width: '15%' }, + type: { title: this.translateService.instant('TYPE'), width: '10%' }, + created: { title: this.translateService.instant('CREATED'), width: '15%' }, + modified: { title: this.translateService.instant('MODIFIED'), width: '15%' }, + Actions: { + name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom', + valuePrepareFunction: (cell: K8SREPODATADISPLAY, row: K8SREPODATADISPLAY): K8SREPODATADISPLAY => row, + renderComponent: K8sActionComponent + } + }; + } + + /** smart table Data Settings @public */ + public generateSettings(): void { + this.settings = { + columns: this.columnList, + actions: { add: false, edit: false, delete: false, position: 'right' }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** smart table listing manipulation @public */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** smart table listing manipulation @public */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'k8-repo' }); + this.dataService.changeMessage(event.data); + } + + /** Compose new K8s Repo Accounts @public */ + public addK8sRepo(): void { + const modalRef: NgbModalRef = this.modalService.open(K8sAddRepoComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } + + /** Generate nsData object from loop and return for the datasource @public */ + // tslint:disable-next-line: typedef + public generateK8sRepoData(k8sRepodata: K8SREPODATA): K8SREPODATADISPLAY { + return { + name: k8sRepodata.name, + identifier: k8sRepodata._id, + url: k8sRepodata.url, + type: k8sRepodata.type, + created: this.sharedService.convertEpochTime(Number(k8sRepodata._admin.created)), + modified: this.sharedService.convertEpochTime(Number(k8sRepodata._admin.modified)), + pageType: 'repo' + }; + } + + /** Fetching the data from server to Load in the smarttable @protected */ + protected generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.K8REPOS_URL).subscribe((k8sRepoDatas: K8SREPODATA[]) => { + this.k8sRepoData = []; + k8sRepoDatas.forEach((k8sRepodata: K8SREPODATA) => { + const k8sRepoDataObj: K8SREPODATADISPLAY = this.generateK8sRepoData(k8sRepodata); + this.k8sRepoData.push(k8sRepoDataObj); + }); + if (this.k8sRepoData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.k8sRepoData).then((data: boolean) => { + this.isLoadingResults = false; + }).catch(() => { + this.isLoadingResults = false; + }); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + +} diff --git a/src/app/layouts/LayoutComponent.html b/src/app/layouts/LayoutComponent.html new file mode 100644 index 0000000..96f1302 --- /dev/null +++ b/src/app/layouts/LayoutComponent.html @@ -0,0 +1,37 @@ + + +
+
+ +
+ +
+
+
+ +
+
+
+
+
+ +
\ No newline at end of file diff --git a/src/app/layouts/LayoutComponent.scss b/src/app/layouts/LayoutComponent.scss new file mode 100644 index 0000000..649387a --- /dev/null +++ b/src/app/layouts/LayoutComponent.scss @@ -0,0 +1,36 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +@import "../../assets/scss/mixins/mixin"; +@import "../../assets/scss/variable"; +#main-wrapper { + @include wh-value(100%, null); + .layout-wrapper { + @include flexbox(flex, null, null, null, stretch, null); + .content-section { + @include wh-value(100%, null); + @include transition(all, 0.3s, null, null); + overflow-x: auto; + } + } + .goto-top { + @include position_value(fixed, null, 30px, 10px, null); + @include font(null, 20px, null); + outline: none; + @include wh-value(40px, 40px); + } +} \ No newline at end of file diff --git a/src/app/layouts/LayoutComponent.ts b/src/app/layouts/LayoutComponent.ts new file mode 100644 index 0000000..b99221a --- /dev/null +++ b/src/app/layouts/LayoutComponent.ts @@ -0,0 +1,44 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file NS Instance Component + */ +import { Component } from '@angular/core'; +import { SharedService } from 'SharedService'; +/** + * Creating component + * @Component takes LayoutComponent.html as template url + */ +@Component({ + selector: 'app-layout', + templateUrl: './LayoutComponent.html', + styleUrls: ['./LayoutComponent.scss'] +}) +/** Exporting a class @exports LayoutComponent */ +export class LayoutComponent { + /** Contains all methods related to shared @private */ + public sharedService: SharedService; + + constructor(sharedService: SharedService) { + this.sharedService = sharedService; + } + /** method to handle go to top event @public */ + public gotoTop(): void { + window.scroll({ behavior: 'smooth', top: 0, left: 0 }); + } +} diff --git a/src/app/layouts/breadcrumb/BreadcrumbComponent.html b/src/app/layouts/breadcrumb/BreadcrumbComponent.html new file mode 100644 index 0000000..9d028b3 --- /dev/null +++ b/src/app/layouts/breadcrumb/BreadcrumbComponent.html @@ -0,0 +1,27 @@ + + \ No newline at end of file diff --git a/src/app/layouts/breadcrumb/BreadcrumbComponent.scss b/src/app/layouts/breadcrumb/BreadcrumbComponent.scss new file mode 100644 index 0000000..7ed3da6 --- /dev/null +++ b/src/app/layouts/breadcrumb/BreadcrumbComponent.scss @@ -0,0 +1,81 @@ +/* +Copyright 2020 TATA ELXSI + +Licensed under the Apache License, Version 2.0 (the 'License'); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +@import "../../../assets/scss/mixins/mixin"; +@import "../../../assets/scss/variable"; +.breadcrumb-holder { + .breadcrumb-custom { + @include box-shadow(0, 2px, 5px, 0, rgba(0, 0, 0, 0.25)); + @include roundedCorners(5); + @include background(null, $white, null, null, null); + @include margin-value(0, 0, 0, 0); + @include flexbox(inline-block, null, null, null, null, null); + overflow: hidden; + .breadcrumb-item-custom { + @include flexbox(block, null, null, null, null, null); + @include line-height(35px); + @include font(null, 12px, null); + @include padding-value(0, 10, 0, 30); + @include position_value(relative, null, null, null, null); + float: left; + cursor: pointer; + &:hover { + @include background(null, $secondary, null, null, null); + a { + color: $white; + } + &::after { + @include background(null, $secondary, null, null, null); + } + } + a { + color: $primary; + } + &:first-child { + padding-left: 20px; + @include roundedTopLeftRadius(5); + @include roundedBottomLeftRadius(5); + &::before { + @include position_value(null, null, null, null, 14px); + } + } + &:last-child { + @include roundedTopRightRadius(5); + @include roundedBottomRightRadius(5); + padding-right: 20px; + &::after { + content: none; + } + } + &::after { + content: ""; + @include position_value(absolute, 0, -18px, null, null); + @include wh-value(36px, 36px); + @include background(null, $white, null, null, null); + @include box-shadow(2px, -2px, 0, 1px, $breadcrumb-after-color); + transform: scale(0.707) rotate(45deg); + z-index: 1; + @include roundedTopRightRadius(5); + @include roundedBottomLeftRadius(50); + } + &.active { + @include background(null, $primary, null, null, null); + color: $white; + } + } + } +} \ No newline at end of file diff --git a/src/app/layouts/breadcrumb/BreadcrumbComponent.ts b/src/app/layouts/breadcrumb/BreadcrumbComponent.ts new file mode 100644 index 0000000..ccb9588 --- /dev/null +++ b/src/app/layouts/breadcrumb/BreadcrumbComponent.ts @@ -0,0 +1,140 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Bread Crumb component. + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { ActivatedRoute, NavigationEnd, Router, RouterEvent, UrlSegment } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { BREADCRUMBITEM } from 'CommonModel'; +import { filter, startWith } from 'rxjs/operators'; +import { isNullOrUndefined } from 'util'; + +/** + * Creating component + * @Component takes BreadcrumbComponent.html as template url + */ +@Component({ + selector: 'app-breadcrumb', + templateUrl: './BreadcrumbComponent.html', + styleUrls: ['./BreadcrumbComponent.scss'] +}) + +/** Exporting a class @exports BreadcrumbComponent */ +export class BreadcrumbComponent implements OnInit { + /** To inject breadCrumb @public */ + public static readonly ROUTE_DATA_BREADCRUMB: string = 'breadcrumb'; + + /** To inject services @public */ + public injector: Injector; + + /** To inject breadCrumb Default icon and url @public */ + public readonly home: {} = { icon: 'pi pi-th-large', url: '/' }; + + /** To inject breadCrumb Menus @public */ + public menuItems: BREADCRUMBITEM[]; + + /** Service holds the router information @private */ + private router: Router; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private activatedRoute: ActivatedRoute; + + /** handle translate service @private */ + private translateService: TranslateService; + + constructor(injector: Injector) { + this.injector = injector; + this.router = this.injector.get(Router); + this.activatedRoute = this.injector.get(ActivatedRoute); + this.translateService = this.injector.get(TranslateService); + } + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.router.events + .pipe(filter((event: RouterEvent) => event instanceof NavigationEnd), startWith(undefined)) + .subscribe(() => this.menuItems = this.createBreadcrumbs(this.activatedRoute.root)); + } + + /** Generate breadcrumbs from data given the module routes @private */ + private createBreadcrumbs(route: ActivatedRoute, url: string = '', breadcrumbs: BREADCRUMBITEM[] = []): + BREADCRUMBITEM[] { + const children: ActivatedRoute[] = route.children; + if (children.length === 0) { + return breadcrumbs; + } + for (const child of children) { + const routeURL: string = child.snapshot.url.map((segment: UrlSegment) => segment.path).join('/'); + if (routeURL !== '') { + url += `/${routeURL}`; + } + let menuLIst: BREADCRUMBITEM[] = child.snapshot.data[BreadcrumbComponent.ROUTE_DATA_BREADCRUMB]; + if (!isNullOrUndefined(menuLIst)) { + menuLIst = JSON.parse(JSON.stringify(menuLIst)); + menuLIst.forEach((item: BREADCRUMBITEM) => { + if (!isNullOrUndefined(item.title)) { + item.title = item.title.replace('{type}', this.checkTitle(item, child.snapshot.params.type)); + item.title = item.title.replace('{id}', child.snapshot.params.id); + item.title = item.title.replace('{project}', localStorage.getItem('project')); + } + if (!isNullOrUndefined(item.url)) { + item.url = item.url.replace('{type}', child.snapshot.params.type); + item.url = item.url.replace('{id}', child.snapshot.params.id); + } + breadcrumbs.push(item); + }); + } + return this.createBreadcrumbs(child, url, breadcrumbs); + } + } + /** Check and update title based on type of operations @private */ + private checkTitle(breadcrumbItem: BREADCRUMBITEM, opertionType: string): string { + if (!isNullOrUndefined(breadcrumbItem.url)) { + if (breadcrumbItem.url.indexOf('packages') !== -1) { + return this.matchPackageTitle(opertionType); + } + if (breadcrumbItem.url.indexOf('instances') !== -1) { + return this.matchInstanceTitle(opertionType); + } + return breadcrumbItem.title; + } + } + /** check and update package title based on package type @private */ + private matchPackageTitle(opertionType: string): string { + if (opertionType === 'ns') { + return this.translateService.instant('NSPACKAGES'); + } else if (opertionType === 'vnf') { + return this.translateService.instant('VNFPACKAGES'); + } else { + return this.translateService.instant('PAGE.DASHBOARD.NETSLICETEMPLATE'); + } + } + /** check and update package title based on instance type @private */ + private matchInstanceTitle(opertionType: string): string { + if (opertionType === 'ns') { + return this.translateService.instant('NSINSTANCES'); + } else if (opertionType === 'vnf') { + return this.translateService.instant('VNFINSTANCES'); + } else if (opertionType === 'pdu') { + return this.translateService.instant('PDUINSTANCES'); + } else { + return this.translateService.instant('PAGE.DASHBOARD.NETSLICEINSTANCE'); + } + } + +} diff --git a/src/app/layouts/header/HeaderComponent.html b/src/app/layouts/header/HeaderComponent.html new file mode 100644 index 0000000..a9ff5ac --- /dev/null +++ b/src/app/layouts/header/HeaderComponent.html @@ -0,0 +1,71 @@ + +Here is a newer {{'APPVERSION' | translate}} {{PACKAGEVERSION}} + of OSM! + + \ No newline at end of file diff --git a/src/app/layouts/header/HeaderComponent.scss b/src/app/layouts/header/HeaderComponent.scss new file mode 100644 index 0000000..86200b7 --- /dev/null +++ b/src/app/layouts/header/HeaderComponent.scss @@ -0,0 +1,51 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +@import '../../../assets/scss/mixins/mixin'; +@import '../../../assets/scss/variable'; + +.navbar{ + @include box-shadow(0px, 0px, 12px, 0px, rgba($black,0.14)); + @include border(all, 0, solid, $gray-80); + .osm-logo{ + cursor: pointer; + } + .custom-dropdown-menu { + .dropdown-item{ + &.project-item{ + @include flexbox(flex, space-between, row, null, center, null); + } + &:active, &:hover, &.active{ + @include background(null, $theme-bg-color, null, null, null); + color: $primary; + } + &.activeProject{ + cursor: default; + } + } + .custom-divider{ + @include wh-value(null, 0); + @include border(top, 2, solid, $primary); + overflow: hidden; + } + } + .osm-version{ + @include background(null, $theme-bg-color, null, null, null); + color: $primary; + cursor: default; + } +} \ No newline at end of file diff --git a/src/app/layouts/header/HeaderComponent.ts b/src/app/layouts/header/HeaderComponent.ts new file mode 100644 index 0000000..4d7f3b1 --- /dev/null +++ b/src/app/layouts/header/HeaderComponent.ts @@ -0,0 +1,106 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ + +/** + * @file Header Component + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { AuthenticationService } from 'AuthenticationService'; +import { environment } from 'environment'; +import { ProjectService } from 'ProjectService'; +import { Observable } from 'rxjs'; +import { SharedService } from 'SharedService'; +import { ProjectRoleMappings, UserDetail } from 'UserModel'; +import { UserSettingsComponent } from 'UserSettingsComponent'; + +/** + * Creating component + * @Component takes HeaderComponent.html as template url + */ +@Component({ + selector: 'app-header', + templateUrl: './HeaderComponent.html', + styleUrls: ['./HeaderComponent.scss'] +}) +/** Exporting a class @exports HeaderComponent */ +export class HeaderComponent implements OnInit { + /** Invoke service injectors @public */ + public injector: Injector; + + /** Variables holds all the projects @public */ + public projectList$: Observable<{}[]>; + + /** Observable holds logined value @public */ + public username$: Observable; + + /** Variables holds admin is logged or not @public */ + public isAdmin: boolean; + + /** Variables holds the selected project @public */ + public selectedProject: Observable; + + /** project @public */ + public getSelectedProject: string; + + /** Version holds packages version @public */ + public PACKAGEVERSION: string; + + /** Contains all methods related to shared @public */ + public sharedService: SharedService; + + /** Utilizes auth service for any auth operations @private */ + private authService: AuthenticationService; + + /** Holds all project details @private */ + private projectService: ProjectService; + + /** Utilizes modal service for any modal operations @private */ + private modalService: NgbModal; + + constructor(injector: Injector) { + this.injector = injector; + this.authService = this.injector.get(AuthenticationService); + this.modalService = this.injector.get(NgbModal); + this.projectService = this.injector.get(ProjectService); + this.sharedService = this.injector.get(SharedService); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.isAdmin = (localStorage.getItem('isAdmin') === 'true') ? true : false; + this.selectedProject = this.authService.ProjectName; + this.authService.ProjectName.subscribe((projectNameFinal: string) => { + this.getSelectedProject = projectNameFinal; + }); + this.username$ = this.authService.username; + this.projectService.setHeaderProjects(); + this.projectList$ = this.projectService.projectList; + this.PACKAGEVERSION = environment.packageVersion; + } + + /** Logout function @public */ + public logout(): void { + this.authService.logout(); + } + + /** Implementation of model for UserSettings options.@public */ + public userSettings(): void { + this.modalService.open(UserSettingsComponent, { backdrop: 'static' }); + } +} diff --git a/src/app/layouts/sidebar/SidebarComponent.html b/src/app/layouts/sidebar/SidebarComponent.html new file mode 100644 index 0000000..1fa4f82 --- /dev/null +++ b/src/app/layouts/sidebar/SidebarComponent.html @@ -0,0 +1,52 @@ + + \ No newline at end of file diff --git a/src/app/layouts/sidebar/SidebarComponent.scss b/src/app/layouts/sidebar/SidebarComponent.scss new file mode 100644 index 0000000..7b5f92e --- /dev/null +++ b/src/app/layouts/sidebar/SidebarComponent.scss @@ -0,0 +1,225 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +@import "../../../assets/scss/mixins/mixin"; +@import "../../../assets/scss/variable"; +$active-margin-left: -230; +$minimize-left: 45px; +.sidebar-mini { + @include transition(all, 0.3s, ease-in-out, 0s); + @include position_value(relative, null, null, null, null); + z-index: 10; + .custom-menu { + @include flexbox(inline-block, null, null, null, null, null); + @include position_value(absolute, 25px, 4px, null, null); + @include margin-value(0, -20, 0, 0); + .btn { + @include wh-value(55px, 55px); + @include roundedCornersPercentage(50%); + @include position_value(relative, null, null, null, null); + @include background(null, transparent, null, null, null); + @include border(all, 0, solid, transparent); + i { + @include margin-value(0, -40, 0, 0); + @include font(null, 14px, null); + } + &.btn-primary { + &:after { + content: ""; + @include position_value(absolute, 0, 0, 0, 0); + @include background(null, $secondary, null, null, null); + @include roundedCorners(10); + @include rotate(45); + z-index: -1; + } + } + &:hover, + &:focus, + &:active { + @include background(null, transparent !important, null, null, null); + } + } + } + nav { + @include wh-value(200px, null); + @include transition(width, 0.2s, ease-in-out, 0s); + ul { + @include padding-value(0, 0, 0, 0); + @include margin-value(0, 0, 0, 0); + @include wh-value(100%, null); + @include background(null, $theme-bg-color, null, null, null); + li { + @include position_value(relative, null, null, null, null); + @include background(null, $secondary, null, null, null); + @include line-height(14px); + list-style-type: none; + cursor: pointer; + color: $white; + &.round-edge-top-3 { + @include roundedTopRightRadius(3); + @include roundedTopLeftRadius(3); + } + &.round-edge-bottom-3 { + @include roundedBottomLeftRadius(3); + @include roundedBottomRightRadius(3); + } + &.border-bottom-none { + @include border(bottom, 0, !important, null); + } + &.header-menu { + @include background(null, $theme-bg-color, null, null, null); + @include padding-value(10, 20, 0, 4); + @include font(null, null, bold); + cursor: default; + .heading { + @include border(bottom, 2, solid, $primary); + @include font(null, 12px, null); + @include line-height(16px); + @include flexbox(block, null, null, null, null, null); + cursor: default; + color: $primary; + } + } + a { + &.individual { + @include padding-value(12, 5, 12, 15); + color: $white; + } + i { + @include wh-value(30px, null); + } + &.parentlink::after { + content: "\f105"; + @include font("Font Awesome 5 Free", null, 900); + @include position_value(absolute, 14px, 15px, null, null); + @include transition(all, 0.3s, ease, 0s); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + } + .sidebar-submenu { + opacity: 0; + @include transition(opacity, 0.4s, ease-in-out, 0s); + li { + @include background(null, $secondary, null, null, null); + @include wh-value(null, 0); + @include transition(height, 200ms, ease-in, 0s); + overflow: hidden; + &:last-child { + @include roundedBottomLeftRadius(4); + @include roundedBottomRightRadius(4); + } + .link { + color: $gray-500; + } + &:hover, + &.active { + .link { + color: $white; + } + } + } + } + &.menu-open { + @include background(null, $primary, null, null, null); + a { + &::after { + @include rotate(90); + } + } + .sidebar-submenu { + opacity: 1; + li { + @include wh-value(null, 45px); + } + } + } + &.parentactive { + @include background(null, $primary, null, null, null); + .parentlink { + color: $white; + } + } + .parentlink, + .link { + @include padding-value(12, 5, 12, 15); + @include flexbox(flex, null, null, null, null, null); + } + } + } + } + &.sidebar-collapse { + @include background(null, transparent, null, null, null); + nav { + transform: translate(0, 0); + @include wh-value($minimize-left, null); + ul { + &.scrollable-menu { + li { + a { + span { + @include position_value(null, 0, null, null, null); + @include padding-value(12, 5, 12, 20); + } + &.individual { + span { + @include roundedBottomRightRadius(4); + } + } + &.parentlink, + &.individual { + span { + @include background(null, $primary, null, null, null); + @include roundedTopRightRadius(4); + } + } + } + &:hover { + .sidebar-submenu { + li { + @include wh-value(null, 45px); + } + } + .sidebar-submenu, + a span { + @include flexbox(block !important, null, null, null, null, null); + @include position_value(absolute, null, null, null, $minimize-left); + @include wh-value(220px, null); + opacity: 1; + } + } + &.header-menu, + .sidebar-submenu { + @include flexbox(none !important, null, null, null, null, null); + transform: translateZ(0); + } + } + } + li { + a::after, + span { + @include flexbox(none !important, null, null, null, null, null); + transform: translateZ(0); + } + } + } + } + } + &.sidebar-open { + @include margin-value(0, 0, 0, $active-margin-left); + } +} \ No newline at end of file diff --git a/src/app/layouts/sidebar/SidebarComponent.ts b/src/app/layouts/sidebar/SidebarComponent.ts new file mode 100644 index 0000000..9cae634 --- /dev/null +++ b/src/app/layouts/sidebar/SidebarComponent.ts @@ -0,0 +1,148 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ + +/** + * @file Sidebar Component + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { DeviceCheckService } from 'DeviceCheckService'; +import { MENU_ITEMS, MENUITEMS } from 'src/models/MenuModel'; +import { isNullOrUndefined } from 'util'; + +/** + * Creating component + * @Component takes SidebarComponent.html as template url + */ +@Component({ + selector: 'app-sidebar', + templateUrl: './SidebarComponent.html', + styleUrls: ['./SidebarComponent.scss'] +}) +/** Exporting a class @exports SidebarComponent */ +export class SidebarComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** submenu router endpoints @public */ + public routerEndpoint: string; + + /** submenu router endpoints @public */ + public getMenus: MENUITEMS[]; + + /** selected Menu @public */ + public selectedItem: string; + + /** get the classlist @public */ + public elementCheck: HTMLCollection; + + /** Apply active class for Desktop @public */ + public classAppliedForDesktop: boolean = false; + + /** Apply active class for mobile @public */ + public classAppliedForMobile: boolean = false; + + /** Device Check service @public */ + public deviceCheckService: DeviceCheckService; + + /** Check for the mobile @public */ + public isMobile$: boolean; + + constructor(injector: Injector) { + this.injector = injector; + this.deviceCheckService = this.injector.get(DeviceCheckService); + this.getMenus = MENU_ITEMS; + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.deviceCheckService.checkDeviceType(); + this.deviceCheckService.isMobile.subscribe((checkIsMobile: boolean) => { + this.isMobile$ = checkIsMobile; + this.getDeviceType(); + }); + } + /** method to open sideBarOpen in all the views */ + public sideBarOpenClose(): void { + if (this.isMobile$) { + this.classAppliedForMobile = !this.classAppliedForMobile; + } else { + this.classAppliedForDesktop = !this.classAppliedForDesktop; + } + this.addClassMainWrapper(); + } + /** Add class for mobile/Desktop in main-wrapper @public */ + public addClassMainWrapper(): void { + const elementMain: HTMLElement = document.querySelector('#main-wrapper'); + if (!isNullOrUndefined(elementMain)) { + if (this.isMobile$) { + elementMain.classList.toggle('sidebar-mobile'); + } else { + elementMain.classList.toggle('sidebar-desktop'); + } + } + } + /** Return the Device type @public */ + public getDeviceType(): void { + if (this.isMobile$) { + this.classAppliedForMobile = true; + this.classAppliedForDesktop = false; + } else { + this.classAppliedForMobile = false; + this.classAppliedForDesktop = false; + } + this.addClassMainWrapper(); + } + /** Set the SideBar Menus click function @public */ + public handleMenuFunction(index: number, method: string, className: string, childExists: boolean): void { + this.selectedItem = method; + if (!isNullOrUndefined(method)) { + this.parentactiveClassAddRemove(index, method, className, childExists); + } + } + /** Removing the parentactive class which is already present and add it to current @public */ + public parentactiveClassAddRemove(index: number, method: string, className: string, childExists: boolean): void { + const element: HTMLElement = document.querySelector('#' + method + '' + index); + const checkOpenedelement: boolean = element.classList.contains(className); + if (!checkOpenedelement) { + this.elementCheck = document.getElementsByClassName(className); + if (this.elementCheck.length > 0) { + this.removeClasses(className); + } + } + if (method !== 'nosubmenu') { + element.classList.toggle(className); + } + if (this.isMobile$ && !childExists) { + this.checkAndCloseSideBar(childExists); + } + } + /** Hide / Show Menus based on the clicking in the menus @public */ + public checkAndCloseSideBar(childExists: boolean): void { + event.stopPropagation(); + if (this.isMobile$ && !childExists) { + this.sideBarOpenClose(); + } + } + /** remove existing Class @public */ + public removeClasses(className: string): void { + this.elementCheck[0].classList.remove(className); + if (this.elementCheck[0]) { + this.removeClasses(className); + } + } +} diff --git a/src/app/login/LoginComponent.html b/src/app/login/LoginComponent.html new file mode 100644 index 0000000..296d51b --- /dev/null +++ b/src/app/login/LoginComponent.html @@ -0,0 +1,57 @@ + + + + diff --git a/src/app/login/LoginComponent.scss b/src/app/login/LoginComponent.scss new file mode 100644 index 0000000..28ac159 --- /dev/null +++ b/src/app/login/LoginComponent.scss @@ -0,0 +1,156 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +@import "../../assets/scss/mixins/mixin"; +@import "../../assets/scss/variable"; + +.login-container { + @include wh-value(100%, 100vh); + @include flexbox(flex, center, null, null, center, null); + @include background(url("../../assets/images/login_background.jpg"), null, cover, no-repeat, center); + background-attachment: fixed; + flex-wrap: wrap; + .wrap-login { + @include background( + linear-gradient( + to left bottom, + #00c0ef, + #00b3f9, + #3ea3fd, + #7190f8, + #9c78e8, + #a86cdd, + #b25fd1, + #bb51c3, + #b151c4, + #a652c6, + #9b53c6, + #9053c7 + ), + null, + null, + null, + null + ); + @include roundedCorners(15); + @include flexbox(flex, space-between, null, null, null, null); + @include position_value(relative, null, null, null, null); + @include box-shadow(0px, 3px, 10px, 0px, rgba($black, 0.5)); + @include padding-value(20, 30, 20, 30); + flex-wrap: wrap; + color: $white; + overflow: visible; + .login-logo { + @include flexbox(flex, center, null, null, center, null); + @include position_value(absolute, -80px, null, null, 95px); + @include box-shadow(1px, 2px, 0px, 0px, $cerise-pink); + @include margin-value(0, 0, 10, 0); + @include wh-value(160px, 150px); + @include background(null, $white, null, null, null); + @include roundedCornersPercentage(50%); + img { + @include wh-value(130px, auto); + @include position_value(null, 50px, null, null, null); + } + } + .login-form { + @include wh-value(290px, null); + @include padding-value(70, 0, 0, 0); + text-align: center; + .wrap-input { + @include position_value(relative, null, null, null, null); + @include wh-value(100%, null); + @include margin-value(0, 0, 10, 0); + z-index: 1; + .input-control { + @include font(null, 15px, null); + @include line-height(1.5); + @include wh-value(100%, 42px); + @include padding-value(0, 30, 0, 65); + @include flexbox(block, null, null, null, null, null); + @include roundedCorners(25); + @include border(all, 0, solid, $gray-80); + @include background(null, $white, null, null, null); + &:focus + .input-icon { + color: $cerise-pink; + @include padding-value(0, 0, 0, 25); + } + } + .input-icon { + @include font(null, 13px, null); + @include flexbox(flex, null, null, null, center, null); + @include position_value(absolute, null, null, 0, 0); + @include wh-value(100%, 100%); + @include padding-value(0, 0, 0, 35); + @include roundedCorners(25); + @include transition(all, 0.5s, null, null); + pointer-events: none; + color: $gray-600; + } + } + .submit-btn { + @include background(null, $white, null, null, null); + @include roundedCornersPercentage(50%); + @include border(all, 0, solid, $gray-80); + @include transition(all, 0.3s, null, null); + @include box-shadow(1px, 5px, 5px, 0px, rgba($black, 0.3), inset); + cursor: pointer; + @include font(null, 25px, null); + @include wh-value(60px, 60px); + @include margin-value(0, 0, 10, 0); + @include padding-value(0, 0, 0, 0); + color: $cerise-pink; + &:hover { + @include box-shadow(1px, 5px, 10px, 0px, rgba($black, 0.3)); + } + .fa { + @include transition(all, 0.4s, null, null); + @include flexbox(block, null, null, null, null, null); + @include padding-value(18, 18, 18, 18); + &:hover { + transform: scale(1.2); + } + } + } + .input-validation-msg { + @include roundedCorners(25); + @include background(null, $cerise-pink, null, null, null); + @include margin-value(0, 0, 10, 0); + @include padding-value(1, 0, 1, 15); + @include font(null, 11px, null); + color: $white; + text-align: left; + } + } + } +} +.login-footer { + @include flexbox(flex, space-between, null, null, center, null); + @include position_value(fixed, null, null, 0px, null); + @include background(null, $purple, null, null, null); + @include wh-value(100%, 40px); + @include padding-value(0, 10, 0, 10); + color: $white; + opacity: 0.9; + a { + @include font(null, null, bold); + color: $white; + &:hover { + text-decoration: underline; + } + } +} \ No newline at end of file diff --git a/src/app/login/LoginComponent.ts b/src/app/login/LoginComponent.ts new file mode 100644 index 0000000..2f4f67e --- /dev/null +++ b/src/app/login/LoginComponent.ts @@ -0,0 +1,131 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ + +/** + * @file Page for Login component + */ +import { HttpErrorResponse } from '@angular/common/http'; +import { Component, Injector, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { AuthenticationService } from 'AuthenticationService'; +import { RestService } from 'RestService'; +import { Observable } from 'rxjs'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; + +/** + * Creating component + * @Component takes LoginComponent.html as template url + */ +@Component({ + selector: 'app-login', + templateUrl: './LoginComponent.html', + styleUrls: ['./LoginComponent.scss'] +}) +/** Exporting a class @exports LoginComponent */ +export class LoginComponent implements OnInit { + /** Invoke service injectors @public */ + public injector: Injector; + + /** contains loginform group information @public */ + public loginForm: FormGroup; + + /** submitted set to boolean state @public */ + public submitted: boolean = false; + + /** contains return URL link @public */ + public returnUrl: string; + + /** Observable Hold the value of subscription @public */ + public isLoggedIn$: Observable; + + /** contains access token information @public */ + public accessToken: string; + + /** Utilizes rest service for any CRUD operations @public */ + public restService: RestService; + + /** Check the loading results @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Contains all methods related to shared @public */ + public sharedService: SharedService; + + /** Utilizes auth service for any auth operations @private */ + private authService: AuthenticationService; + + /** contians form builder module @private */ + private formBuilder: FormBuilder; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + // creates instance of login component + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.authService = this.injector.get(AuthenticationService); + this.formBuilder = this.injector.get(FormBuilder); + this.router = this.injector.get(Router); + this.sharedService = this.injector.get(SharedService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.isLoggedIn$ = this.authService.isLoggedIn; + if (this.isLoggedIn$) { + this.router.navigate(['/']).catch(() => { + // Catch Navigation Error + }); + } + this.loginForm = this.formBuilder.group({ + userName: ['', [Validators.required]], + password: ['', [Validators.required]] + }); + this.returnUrl = isNullOrUndefined(localStorage.getItem('returnUrl')) ? '/' : localStorage.getItem('returnUrl'); + } + + /** + * called on form submit @private onSubmit + */ + public onSubmit(): void { + this.submitted = true; + if (this.loginForm.invalid) { + return; + } + this.isLoadingResults = true; + this.sharedService.cleanForm(this.loginForm); + this.authService.login(this.loginForm.value.userName, this.loginForm.value.password).subscribe( + (data: {}) => { + this.isLoadingResults = false; + this.router.navigate([this.returnUrl]).catch(() => { + // Catch Navigation Error + }); + localStorage.removeItem('returnUrl'); + }, (err: HttpErrorResponse) => { + this.isLoadingResults = false; + this.restService.handleError(err, 'post'); + }); + } +} diff --git a/src/app/packages/PackagesComponent.html b/src/app/packages/PackagesComponent.html new file mode 100644 index 0000000..06b8876 --- /dev/null +++ b/src/app/packages/PackagesComponent.html @@ -0,0 +1,18 @@ + + diff --git a/src/app/packages/PackagesComponent.scss b/src/app/packages/PackagesComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/packages/PackagesComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/packages/PackagesComponent.ts b/src/app/packages/PackagesComponent.ts new file mode 100644 index 0000000..eb9991d --- /dev/null +++ b/src/app/packages/PackagesComponent.ts @@ -0,0 +1,55 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Packages-Component.ts. + */ +import { Component, Injector } from '@angular/core'; +import { Router, RouterEvent } from '@angular/router'; +/** + * Creating Component + * @Component takes PackagesComponent.html as template url + */ +@Component({ + selector: 'app-packages', + templateUrl: './PackagesComponent.html', + styleUrls: ['./PackagesComponent.scss'] +}) +/** Exporting a class @exports PackagesComponent */ +export class PackagesComponent{ + /** Invoke service injectors @public */ + public injector: Injector; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + // creates packages component + constructor(injector: Injector) { + this.injector = injector; + this.router = this.injector.get(Router); + this.router.events.subscribe((event: RouterEvent) => { + this.redirectToList(event.url); + }); + } + + /** Return to list NS Package List */ + public redirectToList(getURL: string): void { + if (getURL === '/packages') { + this.router.navigate(['/packages/ns']).catch(); + } + } +} diff --git a/src/app/packages/PackagesModule.ts b/src/app/packages/PackagesModule.ts new file mode 100644 index 0000000..e6c6628 --- /dev/null +++ b/src/app/packages/PackagesModule.ts @@ -0,0 +1,127 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Packages Module. + */ +import { CommonModule } from '@angular/common'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { ReactiveFormsModule } from '@angular/forms'; +import { FormsModule } from '@angular/forms'; +import { RouterModule, Routes } from '@angular/router'; +import { CodemirrorModule } from '@ctrl/ngx-codemirror'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { TranslateModule } from '@ngx-translate/core'; +import { ClonePackageComponent } from 'ClonePackage'; +import { DataService } from 'DataService'; +import { DragDirective } from 'DragDirective'; +import { EditPackagesComponent } from 'EditPackagesComponent'; +import { LoaderModule } from 'LoaderModule'; +import { NetsliceTemplateComponent } from 'NetsliceTemplate'; +import { SidebarModule } from 'ng-sidebar'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { NSComposerComponent } from 'NSComposer'; +import { NSPackagesComponent } from 'NSPackages'; +import { PackagesComponent } from 'Packages'; +import { PagePerRowModule } from 'PagePerRowModule'; +import { PageReloadModule } from 'PageReloadModule'; +import { ShowContentComponent } from 'ShowContent'; +import { VNFComposerComponent } from 'VNFComposer'; +import { VNFPackagesComponent } from 'VNFPackages'; + +/** To halndle project information */ +const projectInfo: {} = { title: '{project}', url: '/' }; + +/** + * configures routers + */ +const routes: Routes = [ + { + path: '', + component: PackagesComponent, + children: [ + { + path: 'ns', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'NSPACKAGES', url: null }] + }, + component: NSPackagesComponent + }, + { + path: 'vnf', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'VNFPACKAGES', url: null }] + }, + component: VNFPackagesComponent + }, + { + path: 'netslice', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' } + , projectInfo, { title: 'PAGE.DASHBOARD.NETSLICETEMPLATE', url: null }] + }, + component: NetsliceTemplateComponent + }, + { + path: ':type/edit/:id', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' } + , projectInfo, { title: '{type}', url: '/packages/{type}' }, { title: '{id}', url: null }] + }, + component: EditPackagesComponent + }, + { + path: 'ns/compose/:id', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' } + , projectInfo, { title: 'NSPACKAGES', url: '/packages/ns' }, { title: '{id}', url: null }] + }, + component: NSComposerComponent + }, + { + path: 'vnf/compose/:id', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'VNFPACKAGES', url: '/packages/vnf' }, { title: '{id}', url: null }] + }, + component: VNFComposerComponent + } + ] + } +]; + +/** + * Creating @NgModule component for Modules + */ +@NgModule({ + imports: [ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), FormsModule, CommonModule, Ng2SmartTableModule, + CodemirrorModule, TranslateModule, RouterModule.forChild(routes), NgbModule, NgSelectModule, + PagePerRowModule, SidebarModule.forRoot(), LoaderModule, PageReloadModule], + declarations: [PackagesComponent, NSPackagesComponent, VNFPackagesComponent, NetsliceTemplateComponent, + DragDirective, ShowContentComponent, NSComposerComponent, VNFComposerComponent, EditPackagesComponent, ClonePackageComponent], + providers: [DataService], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + entryComponents: [ShowContentComponent, ClonePackageComponent] +}) +/** Exporting a class @exports PackagesModule */ +export class PackagesModule { + /** Variables declared to avoid state-less class */ + private packagesModule: string; +} diff --git a/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.html b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.html new file mode 100644 index 0000000..af52927 --- /dev/null +++ b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.html @@ -0,0 +1,98 @@ + +
+ + + +
+ \ No newline at end of file diff --git a/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.scss b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.scss new file mode 100644 index 0000000..0ecd95d --- /dev/null +++ b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ \ No newline at end of file diff --git a/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.ts b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.ts new file mode 100644 index 0000000..ed5e414 --- /dev/null +++ b/src/app/packages/instantiate-net-slice-template/InstantiateNetSliceTemplateComponent.ts @@ -0,0 +1,282 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file Instantiate NS Modal Component. + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import * as jsyaml from 'js-yaml'; +import { NetworkSliceData } from 'NetworkSliceModel'; +import { NSICREATEPARAMS } from 'NSDModel'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; +import { VimAccountDetails } from 'VimAccountModel'; +/** + * Creating component + * @Component takes InstantiateNetSliceTemplateComponent.html as template url + */ +@Component({ + selector: 'app-instantiate-net-slice-template', + templateUrl: './InstantiateNetSliceTemplateComponent.html', + styleUrls: ['./InstantiateNetSliceTemplateComponent.scss'] +}) +/** Exporting a class @exports InstantiateNetSliceTemplateComponent */ +export class InstantiateNetSliceTemplateComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** FormGroup instance added to the form @ html @public */ + public netSliceInstantiateForm: FormGroup; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Variable set for twoway bindng @public */ + public vimAccountId: string; + + /** Contains all the net slice data collections */ + public netSliceSelect: NetworkSliceData; + + /** Contains all the VIM data collections */ + public vimDetailsSelect: VimAccountDetails; + + /** Variable set for twoway binding @public */ + public netsliceNstId: string; + + /** Form submission Add */ + public submitted: boolean = false; + + /** Check the loading results for loader status @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Element ref for fileInputConfig @public */ + @ViewChild('fileInputConfig', { static: true }) public fileInputConfig: ElementRef; + + /** Element ref for fileInputConfigLabel @public */ + @ViewChild('fileInputConfigLabel', { static: true }) public fileInputConfigLabel: ElementRef; + + /** Element ref for fileInputSSH @public */ + @ViewChild('fileInputSSH', { static: true }) public fileInputSSH: ElementRef; + + /** Element ref for fileInputSSHLabel @public */ + @ViewChild('fileInputSSHLabel', { static: true }) public fileInputSSHLabel: ElementRef; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Utilizes rest service for any CRUD operations @private */ + private restService: RestService; + + /** Utilizes data service for any communication @private */ + private dataService: DataService; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Contains the ssh key to be hosted in dom @private */ + private copySSHKey: string; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + this.formBuilder = this.injector.get(FormBuilder); + this.dataService = this.injector.get(DataService); + this.notifierService = this.injector.get(NotifierService); + this.router = this.injector.get(Router); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + /** Setting up initial value for NSD */ + this.netsliceNstId = ''; + this.dataService.currentMessage.subscribe((event: NetworkSliceData) => { + if (event.identifier !== undefined || event.identifier !== '' || event.identifier !== null) { + this.netsliceNstId = event.identifier; + } + }); + this.netSliceInstantiateFormAction(); + this.headers = new HttpHeaders({ + 'Content-Type': 'application/json', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + /** On Initializing call the methods */ + this.getNetSliceDetails(); + this.getVIMDetails(); + } + + /** Call the netSlice details in the selection options @public */ + public getNetSliceDetails(): void { + this.restService.getResource(environment.NETWORKSLICETEMPLATECONTENT_URL).subscribe((netSlicePackages: NetworkSliceData) => { + this.netSliceSelect = netSlicePackages; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } + + /** Call the VIM details in the selection options @public */ + public getVIMDetails(): void { + this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimDetails: VimAccountDetails) => { + this.vimDetailsSelect = vimDetails; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } + + /** On modal initializing forms @public */ + public netSliceInstantiateFormAction(): void { + this.netSliceInstantiateForm = this.formBuilder.group({ + nsiName: ['', [Validators.required]], + nsiDescription: ['', [Validators.required]], + nstId: ['', [Validators.required]], + vimAccountId: ['', [Validators.required]], + ssh_keys: [null], + config: [null] + }); + } + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.netSliceInstantiateForm.controls; } + + /** On modal submit instantiateNsSubmit will called @public */ + public instantiateNSTSubmit(): void { + this.submitted = true; + this.sharedService.cleanForm(this.netSliceInstantiateForm); + if (this.netSliceInstantiateForm.invalid) { + return; + } + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + if (isNullOrUndefined(this.netSliceInstantiateForm.value.ssh_keys) || this.netSliceInstantiateForm.value.ssh_keys === '') { + delete this.netSliceInstantiateForm.value.ssh_keys; + } else { + this.copySSHKey = JSON.parse(JSON.stringify(this.netSliceInstantiateForm.value.ssh_keys)); + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.netSliceInstantiateForm.get('ssh_keys').setValue(this.copySSHKey); + } + if (isNullOrUndefined(this.netSliceInstantiateForm.value.config) || this.netSliceInstantiateForm.value.config === '') { + delete this.netSliceInstantiateForm.value.config; + } else { + const validJSON: boolean = this.sharedService.checkJson(this.netSliceInstantiateForm.value.config); + if (validJSON) { + this.netSliceInstantiateForm.value.config = JSON.parse(this.netSliceInstantiateForm.value.config); + Object.keys(this.netSliceInstantiateForm.value.config).forEach((item: string) => { + this.netSliceInstantiateForm.value[item] = this.netSliceInstantiateForm.value.config[item]; + }); + delete this.netSliceInstantiateForm.value.config; + } else { + this.notifierService.notify('error', this.translateService.instant('INVALIDCONFIG')); + return; + } + } + this.isLoadingResults = true; + const apiURLHeader: APIURLHEADER = { + url: environment.NETWORKSLICEINSTANCESCONTENT_URL, + httpOptions: { headers: this.headers } + }; + this.restService.postResource(apiURLHeader, this.netSliceInstantiateForm.value) + .subscribe((result: {}) => { + this.activeModal.close(modalData); + this.isLoadingResults = false; + this.notifierService.notify('success', this.netSliceInstantiateForm.value.nsiName + + this.translateService.instant('PAGE.NETSLICE.CREATEDSUCCESSFULLY')); + this.router.navigate(['/instances/netslice']).catch(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + if (!isNullOrUndefined(this.copySSHKey)) { + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.netSliceInstantiateForm.get('ssh_keys').setValue(this.copySSHKey); + } + this.isLoadingResults = false; + }); + } + + /** ssh file process @private */ + public sshFile(files: FileList): void { + if (files && files.length === 1) { + this.sharedService.getFileString(files, 'pub').then((fileContent: string): void => { + const getSSHJson: string = jsyaml.load(fileContent, { json: true }); + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.netSliceInstantiateForm.get('ssh_keys').setValue(getSSHJson); + }).catch((err: string): void => { + if (err === 'typeError') { + this.notifierService.notify('error', this.translateService.instant('PUBFILETYPEERRROR')); + } else { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + this.fileInputSSHLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE'); + this.fileInputSSH.nativeElement.value = null; + }); + } else if (files && files.length > 1) { + this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION')); + } + this.fileInputSSHLabel.nativeElement.innerText = files[0].name; + this.fileInputSSH.nativeElement.value = null; + } + + /** Config file process @private */ + public configFile(files: FileList): void { + if (files && files.length === 1) { + this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => { + const getConfigJson: string = jsyaml.load(fileContent, { json: true }); + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.netSliceInstantiateForm.get('config').setValue(JSON.stringify(getConfigJson)); + }).catch((err: string): void => { + if (err === 'typeError') { + this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR')); + } else { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + this.fileInputConfigLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE'); + this.fileInputConfig.nativeElement.value = null; + }); + } else if (files && files.length > 1) { + this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION')); + } + this.fileInputConfigLabel.nativeElement.innerText = files[0].name; + this.fileInputConfig.nativeElement.value = null; + } + +} diff --git a/src/app/packages/instantiate-ns/InstantiateNsComponent.html b/src/app/packages/instantiate-ns/InstantiateNsComponent.html new file mode 100644 index 0000000..dabd469 --- /dev/null +++ b/src/app/packages/instantiate-ns/InstantiateNsComponent.html @@ -0,0 +1,103 @@ + +
+ + + +
+ \ No newline at end of file diff --git a/src/app/packages/instantiate-ns/InstantiateNsComponent.scss b/src/app/packages/instantiate-ns/InstantiateNsComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/packages/instantiate-ns/InstantiateNsComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/packages/instantiate-ns/InstantiateNsComponent.ts b/src/app/packages/instantiate-ns/InstantiateNsComponent.ts new file mode 100644 index 0000000..515a24b --- /dev/null +++ b/src/app/packages/instantiate-ns/InstantiateNsComponent.ts @@ -0,0 +1,268 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Instantiate NS Modal Component. + */ +import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import * as jsyaml from 'js-yaml'; +import { NSCREATEPARAMS, NSData, NSDDetails } from 'NSDModel'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; +import { VimAccountDetails } from 'VimAccountModel'; + +/** + * Creating component + * @Component takes InstantiateNsComponent.html as template url + */ +@Component({ + selector: 'app-instantiate-ns', + templateUrl: './InstantiateNsComponent.html', + styleUrls: ['./InstantiateNsComponent.scss'] +}) +/** Exporting a class @exports InstantiateNsComponent */ +export class InstantiateNsComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Contains all the nsd data collections */ + public nsdSelect: NSDDetails; + + /** FormGroup instance added to the form @ html @public */ + public instantiateForm: FormGroup; + + /** Contains all vim account collections */ + public vimAccountSelect: VimAccountDetails; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Variable set for twoway binding @public */ + public nsdId: string; + + /** Variable set for twoway bindng @public */ + public vimAccountId: string; + + /** Form submission Add */ + public submitted: boolean = false; + + /** Check the loading results @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Element ref for fileInputConfig @public */ + @ViewChild('fileInputConfig', { static: true }) public fileInputConfig: ElementRef; + + /** Element ref for fileInputConfigLabel @public */ + @ViewChild('fileInputConfigLabel', { static: true }) public fileInputConfigLabel: ElementRef; + + /** Element ref for fileInputSSH @public */ + @ViewChild('fileInputSSH', { static: true }) public fileInputSSH: ElementRef; + + /** Element ref for fileInputSSHLabel @public */ + @ViewChild('fileInputSSHLabel', { static: true }) public fileInputSSHLabel: ElementRef; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Utilizes rest service for any CRUD operations @private */ + private restService: RestService; + + /** Utilizes data service for any communication @private */ + private dataService: DataService; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Contains the ssh key to be hosted in dom @private */ + private copySSHKey: string; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + this.formBuilder = this.injector.get(FormBuilder); + this.dataService = this.injector.get(DataService); + this.notifierService = this.injector.get(NotifierService); + this.router = this.injector.get(Router); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + } + + public ngOnInit(): void { + /** Setting up initial value for NSD */ + this.dataService.currentMessage.subscribe((event: NSData) => { + if (event.identifier !== undefined || event.identifier !== '' || event.identifier !== null) { + this.nsdId = event.identifier; + } + }); + /** On Initializing call the methods */ + this.instantiateFormAction(); + this.getDetailsnsd(); + this.getDetailsvimAccount(); + } + + /** On modal initializing forms @public */ + public instantiateFormAction(): void { + this.instantiateForm = this.formBuilder.group({ + nsName: ['', [Validators.required]], + nsDescription: ['', [Validators.required]], + nsdId: ['', [Validators.required]], + vimAccountId: ['', [Validators.required]], + ssh_keys: [null], + config: [null] + }); + } + + /** Convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.instantiateForm.controls; } + + /** Call the nsd details in the selection options @public */ + public getDetailsnsd(): void { + this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL).subscribe((nsPackages: NSDDetails) => { + this.nsdSelect = nsPackages; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } + + /** Call the vimAccount details in the selection options @public */ + public getDetailsvimAccount(): void { + this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimAccounts: VimAccountDetails) => { + this.vimAccountSelect = vimAccounts; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } + + /** On modal submit instantiateNsSubmit will called @public */ + public instantiateNsSubmit(): void { + this.submitted = true; + this.sharedService.cleanForm(this.instantiateForm); + if (this.instantiateForm.invalid) { + return; + } + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + if (isNullOrUndefined(this.instantiateForm.value.ssh_keys) || this.instantiateForm.value.ssh_keys === '') { + delete this.instantiateForm.value.ssh_keys; + } else { + this.copySSHKey = JSON.parse(JSON.stringify(this.instantiateForm.value.ssh_keys)); + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.instantiateForm.get('ssh_keys').setValue([this.copySSHKey]); + } + if (isNullOrUndefined(this.instantiateForm.value.config) || this.instantiateForm.value.config === '') { + delete this.instantiateForm.value.config; + } else { + const validJSON: boolean = this.sharedService.checkJson(this.instantiateForm.value.config); + if (validJSON) { + this.instantiateForm.value.config = JSON.parse(this.instantiateForm.value.config); + Object.keys(this.instantiateForm.value.config).forEach((item: string) => { + this.instantiateForm.value[item] = this.instantiateForm.value.config[item]; + }); + delete this.instantiateForm.value.config; + } else { + this.notifierService.notify('error', this.translateService.instant('INVALIDCONFIG')); + return; + } + } + const apiURLHeader: APIURLHEADER = { + url: environment.NSINSTANCESCONTENT_URL + }; + this.isLoadingResults = true; + this.restService.postResource(apiURLHeader, this.instantiateForm.value).subscribe((result: {}) => { + this.activeModal.close(modalData); + this.notifierService.notify('success', this.instantiateForm.value.nsName + + this.translateService.instant('PAGE.NSINSTANCE.CREATEDSUCCESSFULLY')); + this.router.navigate(['/instances/ns']).catch(); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'post'); + if (!isNullOrUndefined(this.copySSHKey)) { + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.instantiateForm.get('ssh_keys').setValue(this.copySSHKey); + } + }); + } + + /** ssh file process @private */ + public sshFile(files: FileList): void { + if (files && files.length === 1) { + this.sharedService.getFileString(files, 'pub').then((fileContent: string): void => { + const getSSHJson: string = jsyaml.load(fileContent, { json: true }); + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.instantiateForm.get('ssh_keys').setValue(getSSHJson); + }).catch((err: string): void => { + if (err === 'typeError') { + this.notifierService.notify('error', this.translateService.instant('PUBFILETYPEERRROR')); + } else { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + this.fileInputSSHLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE'); + this.fileInputSSH.nativeElement.value = null; + }); + } else if (files && files.length > 1) { + this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION')); + } + this.fileInputSSHLabel.nativeElement.innerText = files[0].name; + this.fileInputSSH.nativeElement.value = null; + } + + /** Config file process @private */ + public configFile(files: FileList): void { + if (files && files.length === 1) { + this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => { + const getConfigJson: string = jsyaml.load(fileContent, { json: true }); + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.instantiateForm.get('config').setValue(JSON.stringify(getConfigJson)); + }).catch((err: string): void => { + if (err === 'typeError') { + this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR')); + } else { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + this.fileInputConfigLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE'); + this.fileInputConfig.nativeElement.value = null; + }); + } else if (files && files.length > 1) { + this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION')); + } + this.fileInputConfigLabel.nativeElement.innerText = files[0].name; + this.fileInputConfig.nativeElement.value = null; + } +} diff --git a/src/app/packages/netslice-template/NetsliceTemplateComponent.html b/src/app/packages/netslice-template/NetsliceTemplateComponent.html new file mode 100644 index 0000000..6d41e11 --- /dev/null +++ b/src/app/packages/netslice-template/NetsliceTemplateComponent.html @@ -0,0 +1,38 @@ + +
+
{{'PAGE.DASHBOARD.NETSLICETEMPLATE' | translate}}
+
+
+
+ +
+
+ {{'DROPFILES' | translate}}
+
+
+
+
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/packages/netslice-template/NetsliceTemplateComponent.scss b/src/app/packages/netslice-template/NetsliceTemplateComponent.scss new file mode 100644 index 0000000..2c07d1b --- /dev/null +++ b/src/app/packages/netslice-template/NetsliceTemplateComponent.scss @@ -0,0 +1,20 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +.ng2-smart-th.identifier{ + width:40% !important; +} \ No newline at end of file diff --git a/src/app/packages/netslice-template/NetsliceTemplateComponent.ts b/src/app/packages/netslice-template/NetsliceTemplateComponent.ts new file mode 100644 index 0000000..5360518 --- /dev/null +++ b/src/app/packages/netslice-template/NetsliceTemplateComponent.ts @@ -0,0 +1,250 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Netslice-Template. + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, ElementRef, Injector, ViewChild } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { NetslicePackagesActionComponent } from 'NetslicePackagesAction'; +import { NetworkSliceData, NetworkSliceModel } from 'NetworkSliceModel'; +import { LocalDataSource } from 'ng2-smart-table'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes NetsliceTemplateComponent.html as template url + */ +@Component({ + selector: 'app-netslice-template', + templateUrl: './NetsliceTemplateComponent.html', + styleUrls: ['./NetsliceTemplateComponent.scss'] +}) +/** Exporting a class @exports NetsliceTemplateComponent */ +export class NetsliceTemplateComponent { + /** To inject services @public */ + public injector: Injector; + + /** Values for map config @public */ + public selectedRows: object[] = []; + + /** To consume REST API calls @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** handle translate @public */ + public translateService: TranslateService; + + /** Columns list of the smart table @public */ + public columnLists: object = {}; + + /** Settings for smarttable to populate the table with columns @public */ + public settings: object = {}; + + /** Values for map config @public */ + public selectList: object[] = []; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** Element ref for fileInput @public */ + @ViewChild('fileInput', { static: true }) public fileInput: ElementRef; + + /** To consume REST API calls @private */ + private dataService: DataService; + + /** To consume REST API calls @private */ + private restService: RestService; + + /** Formation of appropriate Data for LocalDatasource @private */ + private networkSliceData: NetworkSliceData[] = []; + + /** variables holds file information @private */ + private fileData: string | ArrayBuffer; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.translateService = this.injector.get(TranslateService); + this.notifierService = this.injector.get(NotifierService); + this.sharedService = this.injector.get(SharedService); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.generateColumns(); + this.generateSettings(); + this.generateData(); + this.headers = new HttpHeaders({ + 'Content-Type': 'application/x-yaml', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** smart table Header Colums @public */ + public generateColumns(): void { + this.columnLists = { + name: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' }, + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '50%' }, + usageState: { title: this.translateService.instant('USAGESTATE'), width: '20%' }, + Actions: { + name: 'Actions', width: '10%', filter: false, sort: false, type: 'custom', + title: this.translateService.instant('ACTIONS'), + valuePrepareFunction: (cell: NetworkSliceData, row: NetworkSliceData): NetworkSliceData => row, + renderComponent: NetslicePackagesActionComponent + } + }; + } + + /** smart table Data Settings @public */ + public generateSettings(): void { + this.settings = { + edit: { + editButtonContent: '', + confirmSave: true + }, + delete: { + deleteButtonContent: '', + confirmDelete: true + }, + columns: this.columnLists, + actions: { + add: false, + edit: false, + delete: false, + position: 'right' + }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** smart table listing manipulation @private */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** smart table listing manipulation @private */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'network-slice' }); + this.dataService.changeMessage(event.data); + } + + /** Drag and drop feature and fetchind the details of files @private */ + public filesDropped(files: FileList): void { + if (files && files.length === 1) { + this.isLoadingResults = true; + this.sharedService.getFileString(files, 'yaml').then((fileContent: String | ArrayBuffer): void => { + const apiURLHeader: APIURLHEADER = { + url: environment.NETWORKSLICETEMPLATECONTENT_URL, + httpOptions: { headers: this.headers } + }; + this.saveFileData(apiURLHeader, fileContent); + }).catch((err: string): void => { + this.isLoadingResults = false; + if (err === 'typeError') { + this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR')); + } else { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + }); + } else if (files && files.length > 1) { + this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION')); + } + } + + /** Post the droped files and reload the page @public */ + public saveFileData(urlHeader: APIURLHEADER, fileData: {}): void { + this.fileInput.nativeElement.value = null; + this.restService.postResource(urlHeader, fileData).subscribe((result: {}) => { + this.notifierService.notify('success', this.translateService.instant('PAGE.NETSLICE.TEMPLATECREATEDSUCCESSFULLY')); + this.generateData(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + this.isLoadingResults = false; + }); + } + + /** Generate nsData object from loop and return for the datasource @public */ + public generateNetworkSliceData(networkSlicePackageData: NetworkSliceModel): NetworkSliceData { + return { + name: networkSlicePackageData.name, + identifier: networkSlicePackageData._id, + usageState: networkSlicePackageData._admin.usageState + }; + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } + + /** Fetching the data from server to Load in the smarttable @protected */ + protected generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.NETWORKSLICETEMPLATECONTENT_URL).subscribe((networkSliceList: NetworkSliceModel[]) => { + this.networkSliceData = []; + networkSliceList.forEach((networkSlicePackageData: NetworkSliceModel) => { + const networkSliceDataObj: NetworkSliceData = this.generateNetworkSliceData(networkSlicePackageData); + this.networkSliceData.push(networkSliceDataObj); + }); + if (this.networkSliceData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.networkSliceData).then((data: boolean) => { + this.isLoadingResults = false; + }).catch(() => { + this.isLoadingResults = false; + }); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } +} diff --git a/src/app/packages/ns-packages/NSPackagesComponent.html b/src/app/packages/ns-packages/NSPackagesComponent.html new file mode 100644 index 0000000..5b67268 --- /dev/null +++ b/src/app/packages/ns-packages/NSPackagesComponent.html @@ -0,0 +1,44 @@ + +
+
NS {{'PACKAGES' | translate}}
+ + + +
+
+
+ +
+
+ {{'DROPFILES' | translate}}
+
+
+
+
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/packages/ns-packages/NSPackagesComponent.scss b/src/app/packages/ns-packages/NSPackagesComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/packages/ns-packages/NSPackagesComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/packages/ns-packages/NSPackagesComponent.ts b/src/app/packages/ns-packages/NSPackagesComponent.ts new file mode 100644 index 0000000..46366e0 --- /dev/null +++ b/src/app/packages/ns-packages/NSPackagesComponent.ts @@ -0,0 +1,258 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file NS-Packages component. + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA } from 'CommonModel'; +import { ComposePackages } from 'ComposePackages'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { LocalDataSource } from 'ng2-smart-table'; +import { NSData, NSDDetails } from 'NSDModel'; +import { NsPackagesActionComponent } from 'NsPackagesAction'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes NSPackagesComponent.html as template url + */ +@Component({ + selector: 'app-ns-packages', + templateUrl: './NSPackagesComponent.html', + styleUrls: ['./NSPackagesComponent.scss'] +}) + +/** Exporting a class @exports NSPackagesComponent */ +export class NSPackagesComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Formation of appropriate Data for LocalDatasource @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** handle translate @public */ + public translateService: TranslateService; + + /** Columns list of the smart table @public */ + public columnLists: object = {}; + + /** Settings for smarttable to populate the table with columns @public */ + public settings: object = {}; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** Element ref for fileInput @public */ + @ViewChild('fileInput', { static: true }) public fileInput: ElementRef; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Formation of appropriate Data for LocalDatasource @private */ + private nsData: NSData[] = []; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** variables holds file information @private */ + private fileData: string | ArrayBuffer; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.translateService = this.injector.get(TranslateService); + this.notifierService = this.injector.get(NotifierService); + this.modalService = this.injector.get(NgbModal); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.generateColumns(); + this.generateSettings(); + this.generateData(); + this.headers = new HttpHeaders({ + 'Content-Type': 'application/gzip', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** smart table Header Colums @public */ + public generateColumns(): void { + this.columnLists = { + shortName: { title: this.translateService.instant('SHORTNAME'), width: '15%', sortDirection: 'asc' }, + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' }, + description: { title: this.translateService.instant('DESCRIPTION'), width: '25%' }, + vendor: { title: this.translateService.instant('VENDOR'), width: '15%' }, + version: { title: this.translateService.instant('VERSION'), width: '10%' }, + Actions: { + name: 'Actions', width: '15%', filter: false, sort: false, type: 'custom', + title: this.translateService.instant('ACTIONS'), + valuePrepareFunction: (cell: NSData, row: NSData): NSData => row, renderComponent: NsPackagesActionComponent + } + }; + } + + /** smart table Data Settings @public */ + public generateSettings(): void { + this.settings = { + edit: { + editButtonContent: '', + confirmSave: true + }, + delete: { + deleteButtonContent: '', + confirmDelete: true + }, + columns: this.columnLists, + actions: { + add: false, + edit: false, + delete: false, + position: 'right' + }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** smart table listing manipulation @public */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** smart table listing manipulation @public */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'ns-package' }); + this.dataService.changeMessage(event.data); + } + + /** Drag and drop feature and fetchind the details of files @public */ + public filesDropped(files: FileList): void { + if (files && files.length === 1) { + this.isLoadingResults = true; + this.sharedService.getFileString(files, 'gz').then((fileContent: ArrayBuffer): void => { + const apiURLHeader: APIURLHEADER = { + url: environment.NSDESCRIPTORSCONTENT_URL, + httpOptions: { headers: this.headers } + }; + this.saveFileData(apiURLHeader, fileContent); + }).catch((err: string): void => { + this.isLoadingResults = false; + if (err === 'typeError') { + this.notifierService.notify('error', this.translateService.instant('GZFILETYPEERRROR')); + } else { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + }); + } else if (files && files.length > 1) { + this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION')); + } + } + + /** Post the droped files and reload the page @public */ + public saveFileData(urlHeader: APIURLHEADER, fileData: {}): void { + this.fileInput.nativeElement.value = null; + this.restService.postResource(urlHeader, fileData).subscribe((result: {}) => { + this.notifierService.notify('success', this.translateService.instant('PAGE.NSPACKAGE.CREATEDSUCCESSFULLY')); + this.generateData(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + this.isLoadingResults = false; + }); + } + + /** Generate nsData object from loop and return for the datasource @public */ + public generateNSData(nsdpackagedata: NSDDetails): NSData { + return { + shortName: nsdpackagedata['short-name'], + identifier: nsdpackagedata._id, + description: nsdpackagedata.description, + vendor: nsdpackagedata.vendor, + version: nsdpackagedata.version + }; + } + + /** Fetching the data from server to Load in the smarttable @public */ + public generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL).subscribe((nsdPackageData: NSDDetails[]) => { + this.nsData = []; + nsdPackageData.forEach((nsdpackagedata: NSDDetails) => { + const nsDataObj: NSData = this.generateNSData(nsdpackagedata); + this.nsData.push(nsDataObj); + }); + if (this.nsData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.nsData).then((data: boolean) => { + this.isLoadingResults = false; + }).catch(() => { + this.isLoadingResults = false; + }); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + /** Handle compose new ns package method @public */ + public composeNSPackage(): void { + this.modalService.open(ComposePackages, { backdrop: 'static' }).componentInstance.params = { page: 'ns-package' }; + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } +} diff --git a/src/app/packages/ns-packages/ns-composer/NSComposerComponent.html b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.html new file mode 100644 index 0000000..4e78c16 --- /dev/null +++ b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.html @@ -0,0 +1,264 @@ + + + + + + + + +
+ +
+
+
+
+
+
+
+
+ + {{'PAGE.TOPOLOGY.SELECTELEMENT' | translate}} + +
    +
  • + +  {{'PAGE.TOPOLOGY.VL' | translate}} + +
  • +
+
+
+
+
+
+
+ + {{'PAGE.TOPOLOGY.VNFD' | translate}} + +
    +
  • + +  {{ list['short-name'] }} + +
  • +
+
+
+
+
+
+
+
+
+ + + +
+
+
+ + +
{{'PAGE.TOPOLOGY.VNF' | translate}}
+ + +
{{'PAGE.TOPOLOGY.VL' | translate}}
+ + +
{{'PAGE.TOPOLOGY.CP' | translate}}
+
+
+
+
+ + +
+
+
+
+
+ \ No newline at end of file diff --git a/src/app/packages/ns-packages/ns-composer/NSComposerComponent.scss b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.scss new file mode 100644 index 0000000..d750ccc --- /dev/null +++ b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/packages/ns-packages/ns-composer/NSComposerComponent.ts b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.ts new file mode 100644 index 0000000..082496e --- /dev/null +++ b/src/app/packages/ns-packages/ns-composer/NSComposerComponent.ts @@ -0,0 +1,1089 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file NS Compose Component + */ +// tslint:disable: no-increment-decrement +import { HttpHeaders } from '@angular/common/http'; +import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, CONSTANTNUMBER, ERRORDATA, MODALCLOSERESPONSEDATA, MODALCLOSERESPONSEWITHCP } from 'CommonModel'; +import { ConfirmationTopologyComponent } from 'ConfirmationTopology'; +import * as d3 from 'd3'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import * as HttpStatus from 'http-status-codes'; +import * as jsyaml from 'js-yaml'; +import { COMPOSERNODES, CONSTITUENTVNFD, GRAPHDETAILS, NSDDetails, Tick, TickPath, VLD, VNFDCONNECTIONPOINTREF } from 'NSDModel'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; +import { VNFData, VNFDDetails } from 'VNFDModel'; + +/** + * Creating component + * @Component takes NSComposerComponent.html as template url + */ +@Component({ + selector: 'app-ns-composer', + templateUrl: './NSComposerComponent.html', + styleUrls: ['./NSComposerComponent.scss'], + encapsulation: ViewEncapsulation.None +}) +/** Exporting a class @exports NSComposerComponent */ +export class NSComposerComponent { + /** To inject services @public */ + public injector: Injector; + /** View child contains graphContainer ref @public */ + @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef; + /** dataService to pass the data from one component to another @public */ + public dataService: DataService; + /** Contains VNFD Informations @public */ + public vnfdPackageDetails: VNFData = { identifier: '', shortName: '', vendor: '', description: '', version: '', id: '', name: '' }; + /** Contains VL Details @public */ + public vlDetails: VLD = { + name: '', + 'mgmt-network': true, + 'vim-network-name': '', + type: '', + id: '' + }; + /** Contains the information of the type of modification @public */ + public putType: string; + /** Conatins mousedown action @public */ + public mousedownNode: COMPOSERNODES = null; + /** Conatins mouseup action @public */ + public mouseupNode: COMPOSERNODES = null; + /** Conatins mousedownLink action @public */ + public mousedownLink: COMPOSERNODES = null; + /** Conatins current Selection node action @public */ + public currentSelectedNode: COMPOSERNODES = null; + /** Conatins current Selection node action @public */ + public currentSelectedLink: COMPOSERNODES = null; + /** Need to show the NSD Details @public */ + public isShowNSDDetails: boolean = true; + /** Contains the node information of VL @public */ + public vlNodes: {}[] = []; + /** Need to show the VL Details @public */ + public isShowVLDetails: boolean = false; + /** Contains the node information of VNF @public */ + public vnfNodes: {}[] = []; + /** contains the VNF Details @public */ + public vnfData: CONSTITUENTVNFD; + /** Need to show the VNF Details @public */ + public isShowVNFDetails: boolean = false; + /** Contains the node information of CP @public */ + public cpNodes: {}[] = []; + /** Need to show the CP Details */ + public cpData: VNFDCONNECTIONPOINTREF; + /** Need to show the VNF Details @public */ + public isShowCPDetails: boolean = false; + /** random number count @public */ + public randomNumberLength: number; + /** Contains the vnfd information @public */ + public vnfList: VNFDDetails[] = []; + /** Add the activeclass for the selected @public */ + public activeClass: string = 'active'; + /** Add the fixed class for the freeze @public */ + public fixedClass: string = 'fixed'; + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + /** Get VNF selected node @public */ + public getVNFSelectedData: VNFDDetails[]; + /** Assign the forcesimulation active @public */ + public forceSimulationActive: boolean = false; + /** Assign pinned class for the button when freezed @public */ + public classApplied: boolean = false; + /** Contains sidebar open status @public */ + public sideBarOpened: boolean = false; + /** Contains SVG attributes @private */ + // tslint:disable-next-line:no-any + private svg: any; + /** Contains the Drag line */ + // tslint:disable-next-line: no-any + private dragLine: any; + /** Contains VL node @private */ + // tslint:disable-next-line:no-any + private vlNode: any; + /** Contains VNFD node @private */ + // tslint:disable-next-line:no-any + private vnfdnode: any; + /** Contains CP node @private */ + // tslint:disable-next-line:no-any + private cpnode: any; + /** Rendered nodes represent VL @private */ + // tslint:disable-next-line:no-any + private gvlNode: any; + /** Rendered nodes represent VL @private */ + // tslint:disable-next-line:no-any + private gvnfdNode: any; + /** Rendered nodes represent VL @private */ + // tslint:disable-next-line:no-any + private gcpNode: any; + /** Contains forced node animations @private */ + // tslint:disable-next-line:no-any + private force: any; + /** Contains all the selected node @private */ + private selectedNode: COMPOSERNODES[] = []; + /** variables used for CP @private */ + private iConnectionPointRef: number = 0; + /** Contains the connected point @private */ + private connectionPoint: string; + /** Contains all the NSD information @private */ + private nsd: string; + /** Contains all the VNFD information @private */ + private vnfd: string; + /** Contains id of the node @private */ + private identifier: string; + /** Variables used for cp @private */ + private jConnectionPointRef: number = 0; + /** Contains copy of NSD information @private */ + private nsdCopy: string; + /** Contains the VNFD copy @private */ + private vnfdCopy: string; + /** Contains name of the node @private */ + private name: string; + /** Contains member vnf index value of the node @private */ + private memberVnfIndexValue: number = 0; + /** Contains path information of the node */ + // tslint:disable-next-line:no-any + private path: any; + /** Contains the node information @private */ + private nodes: COMPOSERNODES[] = []; + /** Contains the link information of nodes @private */ + private links: {}[] = []; + /** Contains the NS information @private */ + private nsData: NSDDetails; + /** Instance of the rest service @private */ + private restService: RestService; + /** Service holds the router information @private */ + private router: Router; + /** Holds teh instance of AuthService class of type AuthService @private */ + private activatedRoute: ActivatedRoute; + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + /** Controls the header form @private */ + private headers: HttpHeaders; + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + /** Contains lastkeypressed instance @private */ + private lastKeyDown: number = -1; + /** Instance of the modal service @private */ + private modalService: NgbModal; + /** Setting the Value of connection point refrence of the CP @private */ + private setVnfdConnectionPointRef: string; + /** Setting the Value of VL name for confirmation @private */ + private vlName: string; + /** Setting the Value of VNFD name for confirmation @private */ + private setVnfdName: string; + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + /** Contains selected node VLD objects @private */ + private selectedVLDResult: VLD; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.router = this.injector.get(Router); + this.activatedRoute = this.injector.get(ActivatedRoute); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.modalService = this.injector.get(NgbModal); + this.sharedService = this.injector.get(SharedService); + } + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + // tslint:disable-next-line:no-backbone-get-set-outside-model + this.identifier = this.activatedRoute.snapshot.paramMap.get('id'); + this.generateData(); + this.headers = new HttpHeaders({ + 'Content-Type': 'application/zip', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + } + /** Events handles at drag on D3 region @public */ + // tslint:disable-next-line:no-any + public drag(ev: any): void { + if (ev.target.id === 'vl') { + ev.dataTransfer.setData('text', ev.target.id); + } else { + ev.dataTransfer.setData('text', ev.target.attributes['data-id'].value); + } + } + /** On clicking redirect to NS edit page @public */ + public onEdit(): void { + this.router.navigate(['/packages/ns/edit/', this.identifier]).catch(() => { + // Catch Navigation Error + }); + } + /** Events handles drop at D3 region @public */ + public drop(ev: DragEvent): void { + event.preventDefault(); + this.name = ev.dataTransfer.getData('text'); + if (this.name === 'vl') { + this.svg.selectAll('*').remove(); + this.vldropComposer(); + } else { + this.svg.selectAll('*').remove(); + this.vnfd = ev.dataTransfer.getData('text'); + this.vnfdropComposer(); + } + } + /** Drop VL Composer Data @public */ + public vldropComposer(): void { + this.randomNumberLength = CONSTANTNUMBER.randomNumber; + const generateId: string = 'ns_vl_' + this.randomString( + this.randomNumberLength, + '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + ); + if (this.nsData.vld !== undefined) { + this.nsData.vld.push({ + 'vim-network-name': 'PUBLIC', + name: generateId, + 'mgmt-network': true, + type: 'ELAN', + id: generateId + }); + } else { + Object.assign(this.nsData, { + vld: [{ + 'vim-network-name': 'PUBLIC', + name: generateId, + 'mgmt-network': true, + type: 'ELAN', + id: generateId + }] + }); + } + this.putType = 'nsdadd'; + this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType); + } + /** Drop VNFD Composer Data @public */ + public vnfdropComposer(): void { + if (this.nsData['constituent-vnfd'] !== undefined) { + this.nsData['constituent-vnfd'].push({ + 'vnfd-id-ref': this.vnfd, + 'member-vnf-index': ++this.memberVnfIndexValue + }); + } else { + Object.assign(this.nsData, { + 'constituent-vnfd': [{ + 'vnfd-id-ref': this.vnfd, + 'member-vnf-index': ++this.memberVnfIndexValue + }] + }); + } + this.putType = 'vnfdadd'; + this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType); + } + /** Events handles allow drop on D3 region @public */ + public allowDrop(ev: DragEvent): void { + ev.preventDefault(); + } + /** Save NSD Information @public */ + public saveNSD(): void { + if (this.vnfdPackageDetails.shortName !== undefined) { + this.nsData['short-name'] = this.vnfdPackageDetails.shortName; + } + if (this.vnfdPackageDetails.vendor !== undefined) { + this.nsData.vendor = this.vnfdPackageDetails.vendor; + } + if (this.vnfdPackageDetails.description !== undefined) { + this.nsData.description = this.vnfdPackageDetails.description; + } + if (this.vnfdPackageDetails.version !== undefined) { + this.nsData.version = this.vnfdPackageDetails.version; + } + if (this.vnfdPackageDetails.id !== undefined) { + this.nsData.id = this.vnfdPackageDetails.id; + } + if (this.vnfdPackageDetails.name !== undefined) { + this.nsData.name = this.vnfdPackageDetails.name; + } + this.putType = 'nsdUpdate'; + this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType); + } + /** Save Virtual Link @public */ + public saveVL(vlid: string): void { + this.nsData.vld.forEach((result: VLD) => { + if (result.id === vlid) { + result.name = this.vlDetails.name; + result['mgmt-network'] = !isNullOrUndefined(this.vlDetails['mgmt-network']) ? this.vlDetails['mgmt-network'] : true; + result['vim-network-name'] = !isNullOrUndefined(this.vlDetails['vim-network-name']) ? this.vlDetails['vim-network-name'] : ''; + result.type = this.vlDetails.type; + result.id = this.vlDetails.id; + } + }); + this.putType = 'vlUpdate'; + this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType); + } + /** Add the new Data @public */ + public addData(apiURL: string, identifier: string, data: NSDDetails, putType: string): void { + this.isLoadingResults = true; + let successMessage: string = ''; + if (putType === 'nsdadd') { + successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNSD'; + } else if (putType === 'vnfdadd') { + successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDVNFD'; + } else if (putType === 'cpAdded') { + successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.ADDNS'; + } else if (putType === 'nsdUpdate') { + successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY'; + } else if (putType === 'vlUpdate') { + successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.UPDATEDSUCCESSFULLY'; + } else if (putType === 'nsddelete') { + successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENSD'; + } else if (putType === 'vnfddelete') { + successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETEVNFD'; + } else if (putType === 'nsdelete') { + successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETENS'; + } else if (putType === 'linkdelete') { + successMessage = 'PAGE.NSPACKAGE.NSCOMPOSE.DELETELINK'; + } + /** Below hide for conflicts with light weight UI */ + const apiURLHeader: APIURLHEADER = { + url: apiURL + '/' + identifier + '/nsd_content', + httpOptions: { headers: this.headers } + }; + const nsData: {} = {}; + nsData['nsd:nsd-catalog'] = {}; + nsData['nsd:nsd-catalog'].nsd = []; + nsData['nsd:nsd-catalog'].nsd.push(data); + const descriptorInfo: string = jsyaml.dump(nsData, {sortKeys: true}); + this.sharedService.targzFile({ packageType: 'nsd', id: this.identifier, descriptor: descriptorInfo }) + .then((content: ArrayBuffer): void => { + this.restService.putResource(apiURLHeader, content).subscribe((res: {}) => { + this.generateData(); + this.notifierService.notify('success', this.translateService.instant(successMessage)); + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.generateData(); + this.restService.handleError(error, 'put'); + this.isLoadingResults = false; + }); + }).catch((): void => { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + this.isLoadingResults = false; + }); + } + /** Show Info @public */ + public showInfo(): void { + const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' }); + modalRef.componentInstance.topologyType = 'Info'; + modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.INFO'); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + // empty + } + }).catch(); + } + /** Event to freeze the animation @public */ + public onFreeze(): void { + this.classApplied = !this.classApplied; + const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass); + d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive); + if (alreadyFixedIsActive) { + this.force.stop(); + } + this.forceSimulationActive = alreadyFixedIsActive; + this.nodes.forEach((d: COMPOSERNODES) => { + d.fx = (alreadyFixedIsActive) ? null : d.x; + d.fy = (alreadyFixedIsActive) ? null : d.y; + }); + if (alreadyFixedIsActive) { + this.force.restart(); + } + } + /** Events handles when dragended @public */ + public toggleSidebar(): void { + this.sideBarOpened = !this.sideBarOpened; + this.deselectAllNodes(); + this.showRightSideInfo(true, false, false, false); + } + /** Prepare information for node creation of VNFD @private */ + private generateData(): void { + this.generateVNFData(); + this.generateDataNSDTopology(); + this.sideBarOpened = false; + } + /** Prepare the information of the VNFD @private */ + private generateVNFData(): void { + this.restService.getResource(environment.VNFPACKAGESCONTENT_URL).subscribe((vnfdPackageData: VNFDDetails[]) => { + this.vnfList = vnfdPackageData; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } + /** Prepare information for node creation of NSD Topology @private */ + private generateDataNSDTopology(): void { + this.nodes = []; + this.links = []; + this.iConnectionPointRef = 0; + this.jConnectionPointRef = 0; + this.restService.getResource(environment.NSDESCRIPTORSCONTENT_URL + '/' + this.identifier).subscribe((nsData: NSDDetails) => { + delete nsData._admin; + delete nsData._id; + this.nsData = nsData; + this.vnfdPackageDetails.shortName = nsData['short-name']; + this.vnfdPackageDetails.vendor = nsData.vendor; + this.vnfdPackageDetails.description = nsData.description; + this.vnfdPackageDetails.version = nsData.version; + this.vnfdPackageDetails.id = nsData.id; + this.vnfdPackageDetails.name = nsData.name; + if (nsData.vld !== undefined) { + /** Details of the VL */ + this.nsDataVLD(nsData); + } + if (nsData['constituent-vnfd'] !== undefined) { + /** Details of the VNFD */ + this.nsDataConstituentVNFD(nsData); + } + if (nsData.vld !== undefined) { + this.nsDataVLDLinkCreation(nsData); + } + this.separateAndCreatenode(); + }, (error: ERRORDATA) => { + if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) { + this.router.navigateByUrl('404', { skipLocationChange: true }).catch(); + } else { + this.restService.handleError(error, 'get'); + } + this.isLoadingResults = false; + this.isShowNSDDetails = false; + }); + } + /** nsData-vld undefined Call this function @private */ + private nsDataVLD(nsData: NSDDetails): void { + nsData.vld.forEach((res: VLD) => { + this.nodes.push({ id: res.id, reflexive: false, type: 'vld', name: res.id, selectorId: res.id }); + this.nsd = res.id; + if (res['vnfd-connection-point-ref'] !== undefined) { + res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => { + this.nodes.push( + { + id: this.nsd + ++this.iConnectionPointRef + ':' + result['vnfd-connection-point-ref'], + reflexive: false, + type: 'ns', + name: result['vnfd-connection-point-ref'], + nodeIndex: result['member-vnf-index-ref'], + selectorId: result['vnfd-connection-point-ref'] + '_' + result['member-vnf-index-ref'] + '-osm-' + this.nsd + }); + }); + } + }); + } + /** nsData constituent-vnfd undefined Call this function @private */ + private nsDataConstituentVNFD(nsData: NSDDetails): void { + nsData['constituent-vnfd'].forEach((res: CONSTITUENTVNFD) => { + this.nodes.push( + { + id: res['vnfd-id-ref'] + ':' + res['member-vnf-index'], + reflexive: false, + type: 'vnfd', + name: res['vnfd-id-ref'], + nodeIndex: res['member-vnf-index'], + selectorId: res['vnfd-id-ref'] + '_' + res['member-vnf-index'] + }); + this.vnfd = res['vnfd-id-ref']; + this.memberVnfIndexValue = res['member-vnf-index']; + }); + } + + /** nsData-vld undefined Call this function @private */ + private nsDataVLDLinkCreation(nsData: NSDDetails): void { + nsData.vld.forEach((res: VLD) => { + this.nsdCopy = res.id; + if (res['vnfd-connection-point-ref'] !== undefined) { + this.nsDataVNFDConnectionPointRefrence(res); + } + }); + } + /** nsData-vnfd-connection-point-ref undefined Call this function @private */ + private nsDataVNFDConnectionPointRefrence(res: VLD): void { + res['vnfd-connection-point-ref'].forEach((result: VNFDCONNECTIONPOINTREF) => { + this.connectionPoint = this.nsdCopy + ++this.jConnectionPointRef + ':' + result['vnfd-connection-point-ref']; + this.vnfdCopy = result['vnfd-id-ref'] + ':' + result['member-vnf-index-ref']; + const connectionPointPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.connectionPoint); + const nsdPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.nsdCopy); + const vnfdPos: number = this.nodes.map((e: COMPOSERNODES) => { return e.id; }).indexOf(this.vnfdCopy); + this.links.push( + { + source: this.nodes[connectionPointPos], + target: this.nodes[nsdPos] + }, + { + source: this.nodes[connectionPointPos], + target: this.nodes[vnfdPos] + }); + }); + } + /** Generate random string @private */ + private randomString(length: number, chars: string): string { + let result: string = ''; + for (let randomStringRef: number = length; randomStringRef > 0; --randomStringRef) { + result += chars[Math.floor(Math.random() * chars.length)]; + } + return result; + } + /** Separate and create node @private */ + private separateAndCreatenode(): void { + this.seprateNodes(this.nodes); + this.createnode(this.nodes); + this.isLoadingResults = false; + } + /** Get the default Configuration of containers @private */ + private getGraphContainerAttr(): GRAPHDETAILS { + return { + width: 700, + height: 400, + nodeHeight: 50, + nodeWidth: 35, + textX: -35, + textY: 30, + radius: 5, + distance: 50, + strength: -500, + forcex: 2, + forcey: 2, + sourcePaddingYes: 17, + sourcePaddingNo: 12, + targetPaddingYes: 4, + targetPaddingNo: 3, + alphaTarget: 0.3, + imageX: -25, + imageY: -25, + shiftKeyCode: 17 + }; + } + /** Separate the nodes along with its tyep @private */ + private seprateNodes(node: COMPOSERNODES[]): void { + this.vlNodes = []; this.vnfNodes = []; this.cpNodes = []; + node.forEach((nodeList: COMPOSERNODES) => { + if (nodeList.type === 'vld') { + this.vlNodes.push(nodeList); + } else if (nodeList.type === 'vnfd') { + this.vnfNodes.push(nodeList); + } else if (nodeList.type === 'ns') { + this.cpNodes.push(nodeList); + } + }); + } + /** Node is created and render at D3 region @private */ + private createnode(node: COMPOSERNODES[]): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + d3.selectAll('svg#graphContainer > *').remove(); + d3.select(window).on('keydown', () => { this.keyDown(); }); + d3.select(window).on('keyup', () => { this.keyUp(); }); + this.svg = d3.select('#graphContainer') + .attr('oncontextmenu', 'return false;') + .attr('width', graphContainerAttr.width) + .attr('height', graphContainerAttr.height) + .on('mousemove', () => { this.mousemove(); }); + this.force = d3.forceSimulation() + .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength)) + .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance)) + .force('center', d3.forceCenter(graphContainerAttr.width / graphContainerAttr.forcex, + graphContainerAttr.height / graphContainerAttr.forcey)) + .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex)) + .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey)) + .on('tick', () => { this.tick(); }); + this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0'); + this.path = this.svg.append('svg:g').selectAll('path'); + this.vlNode = this.svg.append('svg:g').selectAll('vlnode'); + this.vnfdnode = this.svg.append('svg:g').selectAll('vnfdnode'); + this.cpnode = this.svg.append('svg:g').selectAll('cpnode'); + // app starts here + this.restart(node); + } + /** update force layout (called automatically each iteration) @private */ + private tick(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + // draw directed edges with proper padding from node centers + this.path.attr('class', 'link').attr('d', (d: Tick) => { + const deltaX: number = d.target.x - d.source.x; + const deltaY: number = d.target.y - d.source.y; + const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY); + const normX: number = deltaX / dist; + const normY: number = deltaY / dist; + const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo; + const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo; + const sourceX: number = d.source.x + (sourcePadding * normX); + const sourceY: number = d.source.y + (sourcePadding * normY); + const targetX: number = d.target.x - (targetPadding * normX); + const targetY: number = d.target.y - (targetPadding * normY); + return `M${sourceX},${sourceY}L${targetX},${targetY}`; + }).on('dblclick', (d: Tick) => { this.getDeleteLinkConfirmation(d); }); + this.vlNode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`); + this.vnfdnode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`); + this.cpnode.attr('transform', (t: TickPath) => `translate(${t.x},${t.y})`); + } + /** Update graph (called when needed) at D3 region @private */ + private restart(node: COMPOSERNODES[]): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.path = this.path.data(this.links); + this.vlNode = this.vlNode.data(this.vlNodes, (d: COMPOSERNODES) => d.id); + this.vnfdnode = this.vnfdnode.data(this.vnfNodes, (d: COMPOSERNODES) => d.id); + this.cpnode = this.cpnode.data(this.cpNodes, (d: COMPOSERNODES) => d.id); + this.resetAndCreateNodes(); + this.force.nodes(node).force('link').links(this.links); + this.force.alphaTarget(graphContainerAttr.alphaTarget).restart(); + } + /** Rest and create nodes @private */ + private resetAndCreateNodes(): void { + this.path.exit().remove(); + this.vlNode.exit().remove(); + this.vnfdnode.exit().remove(); + this.cpnode.exit().remove(); + this.getPathNodes(); + this.getVLNodes(); + this.getVNFDNodes(); + this.getCPNodes(); + this.path.merge(this.path); + this.vlNode = this.gvlNode.merge(this.vlNode); + this.vnfdnode = this.gvnfdNode.merge(this.vnfdnode); + this.cpnode = this.gcpNode.merge(this.cpnode); + } + /** setting the Path @private */ + private getPathNodes(): void { + this.path = this.path.enter().append('svg:path'); + } + /** Setting all the VL nodes @private */ + private getVLNodes(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.gvlNode = this.vlNode.enter().append('svg:g'); + this.gvlNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee'); + this.gvlNode.append('svg:image') + .style('opacity', 1) + .attr('x', graphContainerAttr.imageX) + .attr('y', graphContainerAttr.imageY) + .attr('id', (d: COMPOSERNODES) => { return d.selectorId; }) + .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight) + .attr('xlink:href', 'assets/images/VL.svg') + .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); }) + .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); }) + .on('click', (d: COMPOSERNODES) => { this.singleClick(this.vlNode, d); this.onNodeClickToggleSidebar(); }) + .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); }); + this.gvlNode.append('svg:text') + .attr('class', 'node_text') + .attr('y', graphContainerAttr.textY) + .text((d: COMPOSERNODES) => d.id); + } + /** Setting all the VNFD nodes @private */ + private getVNFDNodes(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.gvnfdNode = this.vnfdnode.enter().append('svg:g'); + this.gvnfdNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee'); + this.gvnfdNode.append('svg:image') + .style('opacity', 1) + .attr('x', graphContainerAttr.imageX) + .attr('y', graphContainerAttr.imageY) + .attr('id', (d: COMPOSERNODES) => { return d.selectorId; }) + .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight) + .attr('xlink:href', 'assets/images/VNFD.svg') + .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); }) + .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); }) + .on('click', (d: COMPOSERNODES) => { this.singleClick(this.vnfdnode, d); this.onNodeClickToggleSidebar(); }) + .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); }); + this.gvnfdNode.append('svg:text') + .attr('class', 'node_text') + .attr('y', graphContainerAttr.textY) + .text((d: COMPOSERNODES) => d.id); + } + /** Setting all the CP nodes @private */ + private getCPNodes(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.gcpNode = this.cpnode.enter().append('svg:g'); + this.gcpNode.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee'); + this.gcpNode.append('svg:image') + .style('opacity', 1) + .attr('x', graphContainerAttr.imageX) + .attr('y', graphContainerAttr.imageY) + .attr('id', (d: COMPOSERNODES) => { return d.selectorId; }) + .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight) + .attr('xlink:href', 'assets/images/CP.svg') + .on('mousedown', (d: COMPOSERNODES) => { this.mouseDown(d); }) + .on('mouseup', (d: COMPOSERNODES) => { this.mouseUp(d); }) + .on('click', (d: COMPOSERNODES) => { this.singleClick(this.cpnode, d); this.onNodeClickToggleSidebar(); }) + .on('dblclick', (d: COMPOSERNODES) => { this.getDeleteConfirmation(d); this.onNodedblClickToggleSidebar(); }); + this.gcpNode.append('svg:text') + .attr('class', 'node_text') + .attr('y', graphContainerAttr.textY) + .text((d: COMPOSERNODES) => d.id); + } + /** Events handles when mousemove it will capture the selected node data @private */ + private mousemove(): void { + if (!this.mousedownNode) { return; } + this.dragLine.attr('d', + `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`); + } + /** Get confirmation Before Deleting the Link in Topology @private */ + private getAddConfirmation(mouseData: COMPOSERNODES, getNsData: NSDDetails, addType: string, getVLDIndex: number): void { + let findVNFName: string = ''; + let findVLDID: string = ''; + if (mouseData.type === 'vld') { + findVNFName = this.mouseupNode.name; + findVLDID = this.mousedownNode.id; + } else { + findVNFName = this.mousedownNode.name; + findVLDID = this.mouseupNode.id; + } + getNsData.vld.forEach((result: VLD) => { + if (result.id === findVLDID) { + this.vlName = result.name; + this.getVNFSelectedData = this.vnfList.filter((vnfList: VNFDDetails) => vnfList.id === findVNFName); + this.setVnfdConnectionPointRef = this.getVNFSelectedData[0]['mgmt-interface'].cp; + this.setVnfdName = this.getVNFSelectedData[0].name; + this.selectedVLDResult = result; + } + }); + if (this.vlName !== undefined && this.setVnfdName !== undefined && this.setVnfdConnectionPointRef !== undefined) { + const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' }); + modalRef.componentInstance.topologyType = 'Add'; + modalRef.componentInstance.cpDetails = this.getVNFSelectedData[0]['connection-point']; + this.translateService.get('PAGE.TOPOLOGY.ADDINGCP', { + vlname: '' + this.vlName + '', + vnfdname: '' + this.setVnfdName + '', + cpname: '' + this.setVnfdConnectionPointRef + '' + }).subscribe((res: string) => { + modalRef.componentInstance.topologyname = res; + }); + modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.CONNECTIONPOINT'); + modalRef.result.then((result: MODALCLOSERESPONSEWITHCP) => { + if (result) { + this.nsData = getNsData; + this.generateCPForVNF(this.selectedVLDResult, result.connection_point, getVLDIndex); + this.addData(environment.NSDESCRIPTORS_URL, this.identifier, getNsData, addType); + } else { + this.deselectPath(); + } + }).catch(); + } else { + this.deselectPath(); + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + } + + /** Generate connection point for vnf using vld @private */ + private generateCPForVNF(result: VLD, cp: string, getVLDIndex: number): void { + if (result['vnfd-connection-point-ref'] !== undefined) { + result['vnfd-connection-point-ref'].push({ + 'member-vnf-index-ref': getVLDIndex, + 'vnfd-connection-point-ref': cp, + 'vnfd-id-ref': this.getVNFSelectedData[0].name + }); + } else { + Object.assign(result, { + 'vnfd-connection-point-ref': [{ + 'member-vnf-index-ref': getVLDIndex, + 'vnfd-connection-point-ref': cp, + 'vnfd-id-ref': this.getVNFSelectedData[0].name + }] + }); + } + } + + /** Events handles when mousedown click it will capture the selected node data @private */ + private mouseDown(d: COMPOSERNODES): void { + event.preventDefault(); + if (d3.event.ctrlKey) { return; } + if (d3.event.shiftKey) { + if (d.type === 'vnfd') { + this.selectedNode.push(d); + } + this.mousedownNode = d; + this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode; + this.currentSelectedLink = null; + this.dragLine.style('marker-end', 'url(#end-arrow)').classed('hidden', false) + .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`); + } + } + /** Event handles when mouseup event occures @private */ + private mouseUp(d: COMPOSERNODES): void { + if (!this.mousedownNode) { return; } + this.dragLine.classed('hidden', true).style('marker-end', ''); + this.mouseupNode = d; + if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vnfd') { + const getOldVLDIndex: string[] = this.mouseupNode.id.split(':'); + const setOldVLDindex: number = +getOldVLDIndex[1]; + this.putType = 'cpAdded'; + this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex); + } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vld') { + const getOldVLDIndex: string[] = this.mousedownNode.id.split(':'); + const setOldVLDindex: number = +getOldVLDIndex[1]; + this.putType = 'cpAdded'; + this.getAddConfirmation(this.mousedownNode, this.nsData, this.putType, setOldVLDindex); + } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'ns') { + this.deselectPath(); + this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP')); + } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'ns') { + this.deselectPath(); + this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNFCP')); + } else if (this.mousedownNode.type === 'vld' && this.mouseupNode.type === 'vld') { + this.deselectPath(); + this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVL')); + } else if (this.mousedownNode.type === 'vnfd' && this.mouseupNode.type === 'vnfd') { + this.deselectPath(); + this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVNF')); + } else if (this.mousedownNode.type === 'ns' && this.mouseupNode.type === 'ns') { + this.deselectPath(); + this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKCP')); + } else { + this.deselectPath(); + this.notifierService.notify('warning', this.translateService.instant('PAGE.NSPACKAGE.NSCOMPOSE.CANNOTLINKVLVNF')); + } + this.resetMouseVars(); + // select new link + this.currentSelectedLink = d; + this.currentSelectedNode = null; + } + /** Mosue Drag Line false if it is not satisfied @private */ + private deselectPath(): void { + this.dragLine.classed('hidden', true).style('marker-end', '').attr('d', 'M0,0L0,0'); + } + /** reset Mouse varaibles @private */ + private resetMouseVars(): void { + this.mousedownNode = null; + this.mouseupNode = null; + this.mousedownLink = null; + } + /** De-select all the selected nodes @private */ + private deselectAllNodes(): void { + this.vlNode.select('image').classed(this.activeClass, false); + this.vnfdnode.select('image').classed(this.activeClass, false); + this.cpnode.select('image').classed(this.activeClass, false); + } + /** Show the right-side information @private */ + private showRightSideInfo(nsdDetails: boolean, vldDetails: boolean, vnfDeails: boolean, cpDetails: boolean): void { + this.isShowNSDDetails = nsdDetails; + this.isShowVLDetails = vldDetails; + this.isShowVNFDetails = vnfDeails; + this.isShowCPDetails = cpDetails; + } + /** Events handles when Shift Click to perform create cp @private */ + // tslint:disable-next-line: no-any + private singleClick(nodeSelected: any, d: COMPOSERNODES): void { + this.selectNodeExclusive(nodeSelected, d); + } + /** Selected nodes @private */ + // tslint:disable-next-line: no-any + private selectNodeExclusive(nodeSeleced: any, d: COMPOSERNODES): void { + const alreadyIsActive: boolean = nodeSeleced.select('#' + d.selectorId).classed(this.activeClass); + this.deselectAllNodes(); + nodeSeleced.select('#' + d.selectorId).classed(this.activeClass, !alreadyIsActive); + if (d.type === 'vld' && !alreadyIsActive) { + this.nsData.vld.forEach((result: VLD) => { + if (result.id === d.id) { + this.showRightSideInfo(false, true, false, false); + this.vlDetails = result; + } + }); + } else if (d.type === 'vnfd' && !alreadyIsActive) { + this.nsData['constituent-vnfd'].forEach((result: CONSTITUENTVNFD) => { + if (result['member-vnf-index'] === d.nodeIndex && result['vnfd-id-ref'] === d.name) { + this.showRightSideInfo(false, false, true, false); + this.vnfData = result; + } + }); + } else if (d.type === 'ns' && !alreadyIsActive) { + this.nsData.vld.forEach((result: VLD) => { + if (result['vnfd-connection-point-ref'] !== undefined) { + result['vnfd-connection-point-ref'].forEach((resultCP: VNFDCONNECTIONPOINTREF) => { + if (resultCP['member-vnf-index-ref'] === d.nodeIndex && resultCP['vnfd-connection-point-ref'] === d.name) { + this.cpData = resultCP; + this.vlDetails = result; + this.showRightSideInfo(false, false, false, true); + } + }); + } + }); + } else { + this.showRightSideInfo(true, false, false, false); + } + } + /** Get confirmation Before Deleting the Link in Topology @private */ + private getDeleteLinkConfirmation(d: Tick): void { + const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' }); + modalRef.componentInstance.topologyType = 'Delete'; + modalRef.componentInstance.topologyname = this.translateService.instant('PAGE.TOPOLOGY.LINK'); + modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.LINK'; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.doubleClickLink(d); + } + }).catch(); + } + /** Events handles when Double Click to Delete the link @private */ + private doubleClickLink(d: Tick): void { + let getID: string = ''; + if (d.target.type === 'vld') { + getID = d.target.id; + } else if (d.source.type === 'vld') { + getID = d.source.id; + } + this.nodes.forEach((res: COMPOSERNODES) => { + if (res.id === getID) { + if (this.nsData.vld !== undefined) { + this.nsData.vld.forEach((vldresult: VLD) => { + if (vldresult.id === getID) { + delete vldresult['vnfd-connection-point-ref']; + } + }); + } + } + }); + this.putType = 'linkdelete'; + this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType); + } + /** Get confirmation Before Deleting the Node in Topology @private */ + private getDeleteConfirmation(d: COMPOSERNODES): void { + const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' }); + modalRef.componentInstance.topologyType = 'Delete'; + modalRef.componentInstance.topologyname = d.name; + if (d.type === 'vld') { + modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VIRTUALLINK'; + } else if (d.type === 'vnfd') { + modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VNF'; + } else if (d.type === 'ns') { + modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT'; + } + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.doubleClick(d); + } + }).catch(); + } + /** Events handles when Double Click to Delete @private */ + private doubleClick(d: COMPOSERNODES): void { + const deletedNode: COMPOSERNODES = d; + this.nodes.forEach((res: COMPOSERNODES) => { + if (res.id === d.id) { + if (deletedNode.type === 'vld') { + const pos: number = this.nsData.vld.map((e: VLD) => { return e.id; }).indexOf(d.id); + this.nsData.vld.splice(pos, 1); + this.putType = 'nsddelete'; + } else if (deletedNode.type === 'vnfd') { + const constituentVNFD: string[] = []; + if (this.nsData['constituent-vnfd'] !== undefined) { + this.nsData['constituent-vnfd'].forEach((ref: CONSTITUENTVNFD) => { + constituentVNFD.push(ref['vnfd-id-ref'] + ':' + ref['member-vnf-index']); + }); + } + const pos: number = constituentVNFD.map((e: string) => { return e; }).indexOf(d.id); + this.nsData['constituent-vnfd'].splice(pos, 1); + const getCP: string[] = d.id.split(':'); + const memberVnfIndexRef: number = +getCP[1]; + const vnfdIDRef: string = getCP[0]; + if (this.nsData.vld !== undefined) { + this.nsData.vld.forEach((resf: VLD) => { + if (resf['vnfd-connection-point-ref'] !== undefined) { + resf['vnfd-connection-point-ref'].forEach((connectionPoint: VNFDCONNECTIONPOINTREF, index: number) => { + if (+connectionPoint['member-vnf-index-ref'] === memberVnfIndexRef && connectionPoint['vnfd-id-ref'] === vnfdIDRef) { + resf['vnfd-connection-point-ref'].splice(index, 1); + } + }); + } + }); + } + this.putType = 'vnfddelete'; + } else if (deletedNode.type === 'ns') { + const getCP: string[] = d.selectorId.split('-osm-'); + const memberVnfIndexRef: number = d.nodeIndex; + const vnfdIDRef: string = getCP[getCP.length - 1]; + if (this.nsData.vld !== undefined) { + this.nsData.vld.forEach((resf: VLD) => { + if (resf['vnfd-connection-point-ref'] !== undefined && resf.id === vnfdIDRef) { + resf['vnfd-connection-point-ref'].forEach((connectionPoint: VNFDCONNECTIONPOINTREF, index: number) => { + if (connectionPoint['member-vnf-index-ref'] === memberVnfIndexRef) { + resf['vnfd-connection-point-ref'].splice(index, 1); + } + }); + } + }); + } + this.putType = 'nsdelete'; + } + this.addData(environment.NSDESCRIPTORS_URL, this.identifier, this.nsData, this.putType); + } + }); + } + /** Key press event @private */ + private keyDown(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + if (this.lastKeyDown !== -1) { return; } + this.lastKeyDown = d3.event.keyCode; + if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) { + this.gvlNode.call(d3.drag() + .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended) + ); + this.gvnfdNode.call(d3.drag() + .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended) + ); + this.gcpNode.call(d3.drag() + .on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended) + ); + this.svg.classed('ctrl', true); + } + } + /** Key realse event @private */ + private keyUp(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.lastKeyDown = -1; + if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) { + this.gvlNode.on('.drag', null); + this.gvnfdNode.on('.drag', null); + this.gcpNode.on('.drag', null); + this.svg.classed('ctrl', false); + } + } + /** Events handles when dragstarted @private */ + private dragstarted(d: COMPOSERNODES): void { + d.fx = d.x; + d.fy = d.y; + } + /** Events handles when dragged @private */ + private dragged(d: COMPOSERNODES): void { + d.fx = d.x = d3.event.x; + d.fy = d.y = d3.event.y; + } + /** Events handles when dragended @private */ + private dragended(d: COMPOSERNODES): void { + if (this.forceSimulationActive) { + d.fx = null; + d.fy = null; + } else { + d.fx = d.x; + d.fy = d.y; + this.forceSimulationActive = false; + } + } + /** Events handles when node double click @private */ + private onNodedblClickToggleSidebar(): void { + this.sideBarOpened = false; + } + /** Events handles when node single click @private */ + private onNodeClickToggleSidebar(): void { + this.sideBarOpened = true; + } +} diff --git a/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.html b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.html new file mode 100644 index 0000000..2aa8f12 --- /dev/null +++ b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.html @@ -0,0 +1,312 @@ + + + + + + + + +
+ +
+
+
+
+
+
+
+
+ + {{'PAGE.TOPOLOGY.SELECTELEMENT' | translate}} + +
    +
  • + +  {{'PAGE.TOPOLOGY.VDU' | translate}} + +
  • +
  • + +  {{'PAGE.TOPOLOGY.CP' | translate}} + +
  • +
  • + +  {{'PAGE.TOPOLOGY.INTVL' | translate}} + +
  • +
+
+
+
+
+
+
+
+
+ + + +
+
+
+ + +
{{'PAGE.TOPOLOGY.VDU' | translate}}
+ + +
{{'PAGE.TOPOLOGY.CP' | translate}}
+ + +
{{'PAGE.TOPOLOGY.INTVL' | translate}}
+ + +
{{'PAGE.TOPOLOGY.INTCP' | translate}}
+
+
+
+
+ + +
+
+
+
+
+ \ No newline at end of file diff --git a/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.scss b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.scss new file mode 100644 index 0000000..4473c67 --- /dev/null +++ b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.ts b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.ts new file mode 100644 index 0000000..21aad71 --- /dev/null +++ b/src/app/packages/ns-packages/vnf-composer/VNFComposerComponent.ts @@ -0,0 +1,1020 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file VNFComposerComponent + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, ElementRef, Injector, ViewChild, ViewEncapsulation } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, GETAPIURLHEADER, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { ConfirmationTopologyComponent } from 'ConfirmationTopology'; +import * as d3 from 'd3'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import * as HttpStatus from 'http-status-codes'; +import * as jsyaml from 'js-yaml'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; +import { + COMPOSERNODES, CONNECTIONPOINT, GRAPHDETAILS, InternalVLD, Tick, TickPath, + VDU, VDUInternalConnectionPoint, VLDInternalConnectionPoint, VNFDInterface, VNFDNODE +} from 'VNFDModel'; + +/** + * Creating component + * @Component takes VNFComposerComponent.html as template url + */ +@Component({ + templateUrl: './VNFComposerComponent.html', + styleUrls: ['./VNFComposerComponent.scss'], + encapsulation: ViewEncapsulation.None +}) +/** Exporting a class @exports VNFComposerComponent */ +export class VNFComposerComponent { + /** To inject services @public */ + public injector: Injector; + /** View child contains graphContainer ref @public */ + @ViewChild('graphContainer', { static: true }) public graphContainer: ElementRef; + /** dataService to pass the data from one component to another @public */ + public dataService: DataService; + /** random number count @public */ + public randomNumberLength: number; + /** Contains the vnfd information @public */ + public vnfList: string[] = []; + /** Contains node type @public */ + public nodeTypeRef: string; + /** Contains VNFD Information @public */ + public vnfdInfo: VNFDNODE = { shortName: '', description: '', version: '', id: '', name: '' }; + /** Contains right panel box information @public */ + public showRightSideInfo: string = ''; + /** Add the fixed class for the freeze @public */ + public fixedClass: string = 'fixed'; + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + /** Assign the forcesimulation active @public */ + public forceSimulationActive: boolean = false; + /** Assign pinned class for the button when freezed @public */ + public classApplied: boolean = false; + /** Contains sidebar open status @public */ + public sideBarOpened: boolean = false; + + /** Contains SVG attributes @private */ + // tslint:disable-next-line:no-any + private svg: any; + /** Contains forced node animations @private */ + // tslint:disable-next-line:no-any + private force: any; + /** Contains the Drag line */ + // tslint:disable-next-line: no-any + private dragLine: any; + /** Contains id of the node @private */ + private identifier: string; + /** Contains path information of the node */ + // tslint:disable-next-line:no-any + private path: any; + /** Contains node network @private */ + // tslint:disable-next-line:no-any + private network: any; + /** Contains node network @private */ + // tslint:disable-next-line:no-any + private virutualDeploymentUnit: any; + /** Contains node connectionPoint @private */ + // tslint:disable-next-line:no-any + private connectionPoint: any; + /** Contains node intConnectionPoint @private */ + // tslint:disable-next-line:no-any + private intConnectionPoint: any; + /** Contains the node information @private */ + private nodes: VNFDNODE[] = []; + /** Contains the link information of nodes @private */ + private links: {}[] = []; + /** Instance of the rest service @private */ + private restService: RestService; + /** Service holds the router information @private */ + private router: Router; + /** Service contails all the shared service information @private */ + private sharedService: SharedService; + /** Holds teh instance of AuthService class of type AuthService @private */ + private activatedRoute: ActivatedRoute; + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + /** Controls the header form @private */ + private headers: HttpHeaders; + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + /** Rendered nodes represent network @private */ + // tslint:disable-next-line:no-any + private gNetwork: any; + /** Rendered nodes represent VDU @private */ + // tslint:disable-next-line:no-any + private gVirutualDeploymentUnit: any; + /** Rendered nodes represent connection point @private */ + // tslint:disable-next-line:no-any + private gConnectionPoint: any; + /** Rendered nodes represent internal connection point @private */ + // tslint:disable-next-line:no-any + private gIntConnectionPoint: any; + /** Contains all the information about VNF Details @private */ + private vnfdPackageDetails: VNFDNODE; + /** Conatins mousedown action @private */ + private mousedownNode: COMPOSERNODES = null; + /** Conatins mouseup action @private */ + private mouseupNode: COMPOSERNODES = null; + /** Conatins current Selection node action @private */ + private currentSelectedNode: COMPOSERNODES = null; + /** Add the activeNode for the selected @private */ + private activeNode: string = 'active'; + /** Contains lastkeypressed instance @private */ + private lastKeyDown: number = -1; + /** Contains VDU Information @private */ + private vduInfo: VDU; + /** Contains Internal VL Information @private */ + private intvlInfo: InternalVLD; + /** Contains Connection Point Information @private */ + private cpInfo: CONNECTIONPOINT; + /** Contains Internal Connection Point Information @private */ + private intcpInfo: VLDInternalConnectionPoint; + /** Instance of the modal service @private */ + private modalService: NgbModal; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.router = this.injector.get(Router); + this.activatedRoute = this.injector.get(ActivatedRoute); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + this.modalService = this.injector.get(NgbModal); + } + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + // tslint:disable-next-line:no-backbone-get-set-outside-model + this.identifier = this.activatedRoute.snapshot.paramMap.get('id'); + this.generateData(); + this.headers = new HttpHeaders({ + 'Content-Type': 'application/zip', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + } + + /** Prepare information for node creation of VNFD @public */ + public generateData(): void { + this.nodes = []; this.links = []; this.vnfdPackageDetails = null; + this.showRightSideInfo = 'vnfdInfo'; + const httpOptions: GETAPIURLHEADER = { + headers: new HttpHeaders({ + 'Content-Type': 'application/zip', + Accept: 'text/plain', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }), + responseType: 'text' + }; + this.restService.getResource(environment.VNFPACKAGES_URL + '/' + this.identifier + '/vnfd', httpOptions) + .subscribe((vnfdPackageDetails: VNFDNODE) => { + try { + const getJson: string = jsyaml.load(vnfdPackageDetails.toString(), { json: true }); + if (getJson.hasOwnProperty('vnfd-catalog')) { + this.vnfdPackageDetails = getJson['vnfd-catalog'].vnfd[0]; + } else if (getJson.hasOwnProperty('vnfd:vnfd-catalog')) { + this.vnfdPackageDetails = getJson['vnfd:vnfd-catalog'].vnfd[0]; + } else if (getJson.hasOwnProperty('vnfd')) { + // tslint:disable-next-line: no-string-literal + this.vnfdPackageDetails = getJson['vnfd'][0]; + } + this.generateCPPoint(this.vnfdPackageDetails); + this.generateVDU(this.vnfdPackageDetails); + this.generateInternalVLD(this.vnfdPackageDetails); + this.generateInternalCP(this.vnfdPackageDetails); + this.generateIntVLCPLinks(this.vnfdPackageDetails); + this.generateVDUCPLinks(this.vnfdPackageDetails); + this.createNode(this.nodes); + this.vnfdInfo.shortName = this.vnfdPackageDetails['short-name']; + this.vnfdInfo.description = this.vnfdPackageDetails.description; + this.vnfdInfo.version = this.vnfdPackageDetails.version; + this.vnfdInfo.id = this.vnfdPackageDetails.id; + this.vnfdInfo.name = this.vnfdPackageDetails.name; + } catch (e) { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + error.error = typeof error.error === 'string' ? jsyaml.load(error.error) : error.error; + if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) { + this.router.navigateByUrl('404', { skipLocationChange: true }).catch(); + } else { + this.restService.handleError(error, 'get'); + } + this.isLoadingResults = false; + this.showRightSideInfo = ''; + }); + } + /** Events handles at drag on D3 region @public */ + // tslint:disable-next-line:no-any + public drag(ev: any): void { + ev.dataTransfer.setData('text', ev.target.id); + } + /** Events handles drop at D3 region @public */ + public drop(ev: DragEvent): void { + ev.preventDefault(); + this.nodeTypeRef = ev.dataTransfer.getData('text'); + if (this.nodeTypeRef === 'vdu') { + this.svg.selectAll('*').remove(); + this.vduDropCompose(); + } else if (this.nodeTypeRef === 'cp') { + this.svg.selectAll('*').remove(); + this.cpDropCompose(); + } else if (this.nodeTypeRef === 'intvl') { + this.svg.selectAll('*').remove(); + this.intvlDropCompose(); + } + } + /** Events handles allow drop on D3 region @public */ + public allowDrop(ev: DragEvent): void { + ev.preventDefault(); + } + /** Generate and list CP points @public */ + public generateCPPoint(vnfdPackageDetails: VNFDNODE): void { + if (vnfdPackageDetails['connection-point'] !== undefined) { + vnfdPackageDetails['connection-point'].forEach((cp: CONNECTIONPOINT) => { + this.nodes.push({ id: cp.name, nodeTypeRef: 'cp', name: cp.name, type: cp.type }); + }); + } + } + /** Generate and list VDU @public */ + public generateVDU(vnfdPackageDetails: VNFDNODE): void { + if (vnfdPackageDetails.vdu !== undefined) { + vnfdPackageDetails.vdu.forEach((vdu: VDU) => { + this.nodes.push({ + id: vdu.name, nodeTypeRef: 'vdu', 'cloud-init-file': vdu['cloud-init-file'], count: vdu.count, description: vdu.description, + vduID: vdu.id, name: vdu.name, interface: vdu.interface, 'vm-flavor': vdu['vm-flavor'] + }); + }); + } + } + /** Generate and list Internal VLD @public */ + public generateInternalVLD(vnfdPackageDetails: VNFDNODE): void { + if (vnfdPackageDetails['internal-vld'] !== undefined) { + vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => { + this.nodes.push({ + id: internalVLD.name, nodeTypeRef: 'intvl', intVLID: internalVLD.id, + 'internal-connection-point': internalVLD['internal-connection-point'], + 'ip-profile-ref': internalVLD['ip-profile-ref'], name: internalVLD.name, 'short-name': internalVLD['short-name'], + type: internalVLD.type + }); + }); + } + } + /** Generate and list Internal CP @public */ + public generateInternalCP(vnfdPackageDetails: VNFDNODE): void { + if (vnfdPackageDetails.vdu !== undefined) { + vnfdPackageDetails.vdu.forEach((intCP: VDUInternalConnectionPoint) => { + if (intCP['internal-connection-point'] !== undefined) { + intCP['internal-connection-point'].forEach((internalCP: VDUInternalConnectionPoint) => { + this.nodes.push({ + id: internalCP.name, nodeTypeRef: 'intcp', name: internalCP.name, + 'short-name': internalCP['short-name'], type: internalCP.type + }); + }); + } + }); + } + } + /** Generate VDU External and Internal CP Links @public */ + public generateVDUCPLinks(vnfdPackageDetails: VNFDNODE): void { + if (vnfdPackageDetails.vdu !== undefined) { + vnfdPackageDetails.vdu.forEach((vdu: VDU) => { + const vduLink: string = vdu.name; + if (vdu.interface !== undefined) { + vdu.interface.forEach((interfaceDetails: VNFDInterface) => { + if (interfaceDetails['external-connection-point-ref'] !== undefined) { + this.links.push({ source: vduLink, target: interfaceDetails['external-connection-point-ref'] }); + } + if (interfaceDetails['internal-connection-point-ref'] !== undefined) { + this.links.push({ source: vduLink, target: interfaceDetails['internal-connection-point-ref'] }); + } + }); + } + }); + } + } + /** Generate Network/VLD/Internal VirtualLink, CP Links @public */ + public generateIntVLCPLinks(vnfdPackageDetails: VNFDNODE): void { + if (vnfdPackageDetails['internal-vld'] !== undefined) { + vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => { + const vldName: string = internalVLD.name; + if (internalVLD['internal-connection-point'] !== undefined) { + internalVLD['internal-connection-point'].forEach((intCP: VLDInternalConnectionPoint) => { + this.links.push({ source: vldName, target: intCP['id-ref'] }); + }); + } + }); + } + } + /** VNFD details can be saved on users inputs @public */ + public saveVNFD(): void { + this.vnfdPackageDetails['short-name'] = this.vnfdInfo.shortName; + this.vnfdPackageDetails.description = this.vnfdInfo.description; + this.vnfdPackageDetails.version = this.vnfdInfo.version; + this.vnfdPackageDetails.id = this.vnfdInfo.id; + this.vnfdPackageDetails.name = this.vnfdInfo.name; + this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails); + delete this.vnfdPackageDetails.shortName; + } + /** VDU details can be saved on users inputs @public */ + public saveVDU(vduID: string): void { + this.vnfdPackageDetails.vdu.forEach((ref: VDU) => { + if (ref.id === vduID) { + ref.count = this.vduInfo.count; + ref.description = this.vduInfo.description; + ref.image = this.vduInfo.image; + ref.id = this.vduInfo.id; + ref.name = this.vduInfo.name; + } + }); + this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails); + } + /** IntVL details can be saved on users inputs @public */ + public saveIntVL(intVLID: string): void { + this.vnfdPackageDetails['internal-vld'].forEach((ref: InternalVLD) => { + if (ref.id === intVLID) { + ref['short-name'] = this.intvlInfo.shortName; + ref.name = this.intvlInfo.name; + ref.type = this.intvlInfo.type; + ref['ip-profile-ref'] = !isNullOrUndefined(this.intvlInfo.ipProfileRef) ? this.intvlInfo.ipProfileRef : ''; + ref.id = this.intvlInfo.id; + delete ref.shortName; + delete ref.ipProfileRef; + } + }); + this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails); + } + /** IntVL details can be saved on users inputs @public */ + public saveCP(cpName: string): void { + this.vnfdPackageDetails['connection-point'].forEach((ref: CONNECTIONPOINT) => { + if (ref.name === cpName) { + if (!isNullOrUndefined(this.cpInfo.type)) { + ref.type = this.cpInfo.type; + } + ref.name = this.cpInfo.name; + } + }); + this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails); + } + /** Edit topology @public */ + public onEdit(): void { + this.router.navigate(['/packages/vnf/edit/', this.identifier]).catch(); + } + /** Show Info @public */ + public showInfo(): void { + const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' }); + modalRef.componentInstance.topologyType = 'Info'; + modalRef.componentInstance.topologytitle = this.translateService.instant('PAGE.TOPOLOGY.INFO'); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + // empty + } + }).catch(); + } + /** Event to freeze the animation @public */ + public onFreeze(): void { + this.classApplied = !this.classApplied; + const alreadyFixedIsActive: boolean = d3.select('svg#graphContainer').classed(this.fixedClass); + d3.select('svg#graphContainer').classed(this.fixedClass, !alreadyFixedIsActive); + if (alreadyFixedIsActive) { + this.force.stop(); + } + this.forceSimulationActive = alreadyFixedIsActive; + this.nodes.forEach((d: COMPOSERNODES) => { + d.fx = (alreadyFixedIsActive) ? null : d.x; + d.fy = (alreadyFixedIsActive) ? null : d.y; + }); + if (alreadyFixedIsActive) { + this.force.restart(); + } + } + /** Events handles when dragended @public */ + public toggleSidebar(): void { + this.sideBarOpened = !this.sideBarOpened; + this.deselectAllNodes(); + this.showRightSideInfo = 'vnfdInfo'; + } + /** Get the default Configuration of containers @private */ + private getGraphContainerAttr(): GRAPHDETAILS { + return { + width: 700, + height: 400, + nodeHeight: 50, + nodeWidth: 35, + textX: -35, + textY: 30, + radius: 5, + distance: 50, + strength: -500, + forcex: 2, + forcey: 2, + sourcePaddingYes: 17, + sourcePaddingNo: 12, + targetPaddingYes: 4, + targetPaddingNo: 3, + alphaTarget: 0.3, + imageX: -25, + imageY: -25, + shiftKeyCode: 17 + }; + } + /** Node is created and render at D3 region @private */ + private createNode(nodes: VNFDNODE[]): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + d3.selectAll('svg#graphContainer > *').remove(); + d3.select(window).on('keydown', () => { this.keyDown(); }); + d3.select(window).on('keyup', () => { this.keyUp(); }); + this.svg = d3.select('#graphContainer').attr('oncontextmenu', 'return false;').attr('width', graphContainerAttr.width) + .attr('height', graphContainerAttr.height).on('mousemove', () => { this.mousemove(); }); + this.force = d3.forceSimulation() + .force('link', d3.forceLink().id((d: TickPath) => d.id).distance(graphContainerAttr.distance)) + .force('charge', d3.forceManyBody().strength(graphContainerAttr.strength)) + .force('x', d3.forceX(graphContainerAttr.width / graphContainerAttr.forcex)) + .force('y', d3.forceY(graphContainerAttr.height / graphContainerAttr.forcey)) + .on('tick', () => { this.tick(); }); + this.path = this.svg.append('svg:g').selectAll('path'); + this.dragLine = this.svg.append('svg:path').attr('class', 'link dragline hidden').attr('d', 'M0,0L0,0'); + this.network = this.svg.append('svg:g').selectAll('network'); + this.virutualDeploymentUnit = this.svg.append('svg:g').selectAll('virutualDeploymentUnit'); + this.connectionPoint = this.svg.append('svg:g').selectAll('connectionPoint'); + this.intConnectionPoint = this.svg.append('svg:g').selectAll('intConnectionPoint'); + this.restart(nodes); + } + /** Update force layout (called automatically each iteration) @private */ + private tick(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.path.attr('d', (d: Tick) => { + const deltaX: number = d.target.x - d.source.x; const deltaY: number = d.target.y - d.source.y; + const dist: number = Math.sqrt(deltaX * deltaX + deltaY * deltaY); + const normX: number = deltaX / dist; const normY: number = deltaY / dist; + const sourcePadding: number = d.left ? graphContainerAttr.sourcePaddingYes : graphContainerAttr.sourcePaddingNo; + const targetPadding: number = d.right ? graphContainerAttr.targetPaddingYes : graphContainerAttr.targetPaddingNo; + const sourceX: number = d.source.x + (sourcePadding * normX); const sourceY: number = d.source.y + (sourcePadding * normY); + const targetX: number = d.target.x - (targetPadding * normX); const targetY: number = d.target.y - (targetPadding * normY); + return `M${sourceX},${sourceY}L${targetX},${targetY}`; + }).on('dblclick', (d: Tick) => { this.getDeleteLinkConfirmation(d); }); + this.network.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`); + this.virutualDeploymentUnit.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`); + this.connectionPoint.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`); + this.intConnectionPoint.attr('transform', (d: TickPath) => `translate(${d.x},${d.y})`); + } + /** Update graph (called when needed) @private */ + private restart(nodes: VNFDNODE[]): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.path = this.path.data(this.links); + const cpNodes: {}[] = []; const vduNodes: {}[] = []; const vlNodes: {}[] = []; const intcpNodes: {}[] = []; //Nodes are known by id + nodes.forEach((nodeList: VNFDNODE) => { + if (nodeList.nodeTypeRef === 'cp') { cpNodes.push(nodeList); } + else if (nodeList.nodeTypeRef === 'vdu') { vduNodes.push(nodeList); } + else if (nodeList.nodeTypeRef === 'intvl') { vlNodes.push(nodeList); } + else if (nodeList.nodeTypeRef === 'intcp') { intcpNodes.push(nodeList); } + }); + this.network = this.network.data(vlNodes, (d: { id: number }) => d.id); + this.virutualDeploymentUnit = this.virutualDeploymentUnit.data(vduNodes, (d: { id: number }) => d.id); + this.connectionPoint = this.connectionPoint.data(cpNodes, (d: { id: number }) => d.id); + this.intConnectionPoint = this.intConnectionPoint.data(intcpNodes, (d: { id: number }) => d.id); + this.resetAndCreateNodes(); + this.force.nodes(nodes).force('link').links(this.links); //Set the graph in motion + this.force.alphaTarget(graphContainerAttr.alphaTarget).restart(); + } + /** Rest and create nodes @private */ + private resetAndCreateNodes(): void { + this.path.exit().remove(); + this.network.exit().remove(); + this.virutualDeploymentUnit.exit().remove(); + this.connectionPoint.exit().remove(); + this.intConnectionPoint.exit().remove(); + this.getPathNodes(); + this.getgNetwork(); + this.getgVirutualDeploymentUnit(); + this.getgConnectionPoint(); + this.getgIntConnectionPoint(); + this.network = this.gNetwork.merge(this.network); + this.virutualDeploymentUnit = this.gVirutualDeploymentUnit.merge(this.virutualDeploymentUnit); + this.connectionPoint = this.gConnectionPoint.merge(this.connectionPoint); + this.intConnectionPoint = this.gIntConnectionPoint.merge(this.intConnectionPoint); + this.path.merge(this.path); + } + /** Setting the Path @private */ + private getPathNodes(): void { + this.path = this.path.enter().append('svg:path').attr('class', 'link'); + } + /** Settings all the network attributes of nodes @private */ + private getgNetwork(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.gNetwork = this.network.enter().append('svg:g'); + this.gNetwork.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee'); + this.gNetwork.append('svg:image') + .style('opacity', 1) + .attr('x', graphContainerAttr.imageX) + .attr('y', graphContainerAttr.imageY) + .attr('id', (d: VNFDNODE) => { return d.id; }) + .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight) + .attr('xlink:href', 'assets/images/INTVL.svg') + .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); }) + .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); }) + .on('click', (d: VNFDNODE) => { this.singleClick(this.network, d); this.onNodeClickToggleSidebar(); }) + .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); }); + this.gNetwork.append('svg:text') + .attr('class', 'node_text') + .attr('y', graphContainerAttr.textY) + .text((d: { id: number }) => d.id); + } + /** Settings all the connection point attributes of nodes @private */ + private getgVirutualDeploymentUnit(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.gVirutualDeploymentUnit = this.virutualDeploymentUnit.enter().append('svg:g'); + this.gVirutualDeploymentUnit.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee'); + this.gVirutualDeploymentUnit.append('svg:image') + .style('opacity', 1) + .attr('x', graphContainerAttr.imageX) + .attr('y', graphContainerAttr.imageY) + .attr('id', (d: VNFDNODE) => { return d.id; }) + .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight) + .attr('xlink:href', 'assets/images/VDU.svg') + .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); }) + .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); }) + .on('click', (d: VNFDNODE) => { this.singleClick(this.virutualDeploymentUnit, d); this.onNodeClickToggleSidebar(); }) + .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); }); + this.gVirutualDeploymentUnit.append('svg:text') + .attr('class', 'node_text') + .attr('y', graphContainerAttr.textY) + .text((d: { id: string }) => d.id); + } + /** Settings all the connection point attributes of nodes @private */ + private getgConnectionPoint(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.gConnectionPoint = this.connectionPoint.enter().append('svg:g'); + this.gVirutualDeploymentUnit.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee'); + this.gConnectionPoint.append('svg:image') + .style('opacity', 1) + .attr('x', graphContainerAttr.imageX) + .attr('y', graphContainerAttr.imageY) + .attr('id', (d: VNFDNODE) => { return d.id; }) + .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight) + .attr('xlink:href', 'assets/images/CP-VNF.svg') + .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); }) + .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); }) + .on('click', (d: VNFDNODE) => { this.singleClick(this.connectionPoint, d); this.onNodeClickToggleSidebar(); }) + .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); }); + this.gConnectionPoint.append('svg:text') + .attr('class', 'node_text') + .attr('y', graphContainerAttr.textY) + .text((d: { id: string }) => d.id); + } + /** Settings all the internal connection point attributes of nodes @private */ + private getgIntConnectionPoint(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.gIntConnectionPoint = this.intConnectionPoint.enter().append('svg:g'); + this.gIntConnectionPoint.append('svg:circle').attr('r', graphContainerAttr.radius).style('fill', '#eeeeee'); + this.gIntConnectionPoint.append('svg:image') + .style('opacity', 1) + .attr('x', graphContainerAttr.imageX) + .attr('y', graphContainerAttr.imageY) + .attr('id', (d: VNFDNODE) => { return d.id; }) + .attr('class', 'node').attr('width', graphContainerAttr.nodeWidth).attr('height', graphContainerAttr.nodeHeight) + .attr('xlink:href', 'assets/images/INTCP.svg') + .on('mousedown', (d: VNFDNODE) => { this.mouseDown(d); }) + .on('mouseup', (d: VNFDNODE) => { this.mouseUp(d); }) + .on('click', (d: VNFDNODE) => { this.singleClick(this.intConnectionPoint, d); this.onNodeClickToggleSidebar(); }) + .on('dblclick', (d: VNFDNODE) => { this.getDeleteNodeConfirmation(d); this.onNodedblClickToggleSidebar(); }); + this.gIntConnectionPoint.append('svg:text') + .attr('class', 'node_text') + .attr('y', graphContainerAttr.textY) + .text((d: { id: string }) => d.id); + } + /** Drop VDU Composer Data @private */ + private vduDropCompose(): void { + const randomID: string = this.sharedService.randomString(); + const vduNode: VNFDNODE[] = [{ + nodeTypeRef: 'vdu', id: 'vdu_' + randomID, count: 1, description: '', name: 'vdu_' + randomID, image: 'ubuntu', + interface: [], 'internal-connection-point': [], 'monitoring-param': [], 'vm-flavor': {} + }]; + const nodeCopy: VNFDNODE[] = this.nodes; + Array.prototype.push.apply(vduNode, nodeCopy); + this.nodes = vduNode; + if (this.vnfdPackageDetails.vdu === undefined) { this.vnfdPackageDetails.vdu = []; } + this.vnfdPackageDetails.vdu.push({ + id: 'vdu_' + randomID, count: 1, description: '', name: 'vdu_' + randomID, image: 'ubuntu', + interface: [], 'internal-connection-point': [], 'monitoring-param': [], 'vm-flavor': {} + }); + this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails); + } + /** Drop CP Composer Data @private */ + private cpDropCompose(): void { + const randomID: string = this.sharedService.randomString(); + const cpNode: VNFDNODE[] = [{ nodeTypeRef: 'cp', id: 'cp_' + randomID, name: 'cp_' + randomID }]; + const nodeCopy: VNFDNODE[] = this.nodes; + Array.prototype.push.apply(cpNode, nodeCopy); + this.nodes = cpNode; + if (this.vnfdPackageDetails['connection-point'] === undefined) { + this.vnfdPackageDetails['connection-point'] = []; + } + this.vnfdPackageDetails['connection-point'].push({ + id: 'cp_' + randomID, name: 'cp_' + randomID, type: 'VPORT' + }); + this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails); + } + /** Drop IntVL Composer Data @private */ + private intvlDropCompose(): void { + const randomID: string = this.sharedService.randomString(); + const intvlNode: VNFDNODE[] = [{ + nodeTypeRef: 'intvl', id: 'vnf_vl_' + randomID, name: 'vnf_vl_' + randomID, 'short-name': 'vnf_vl_' + randomID, 'ip-profile-ref': '', + type: 'ELAN' + }]; + const nodeCopy: VNFDNODE[] = this.nodes; + Array.prototype.push.apply(intvlNode, nodeCopy); + this.nodes = intvlNode; + if (this.vnfdPackageDetails['internal-vld'] === undefined) { this.vnfdPackageDetails['internal-vld'] = []; } + this.vnfdPackageDetails['internal-vld'].push({ + id: 'vnf_vl_' + randomID, name: 'vnf_vl_' + randomID, 'short-name': 'vnf_vl_' + randomID, + 'ip-profile-ref': '', type: 'ELAN', 'internal-connection-point': [] + }); + this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails); + } + /** Add the Add Nodes Data @private */ + private addNodes(apiURL: string, identifier: string, data: VNFDNODE): void { + this.isLoadingResults = true; + const apiURLHeader: APIURLHEADER = { + url: apiURL + '/' + identifier + '/package_content', + httpOptions: { headers: this.headers } + }; + const vnfData: {} = {}; + vnfData['vnfd:vnfd-catalog'] = {}; + vnfData['vnfd:vnfd-catalog'].vnfd = []; + vnfData['vnfd:vnfd-catalog'].vnfd.push(data); + const descriptorInfo: string = jsyaml.dump(vnfData, { sortKeys: true }); + this.sharedService.targzFile({ packageType: 'vnfd', id: this.identifier, descriptor: descriptorInfo }) + .then((content: ArrayBuffer): void => { + this.restService.putResource(apiURLHeader, content).subscribe((res: {}) => { + this.generateData(); + this.notifierService.notify('success', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.UPDATEDSUCCESSFULLY')); + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.generateData(); + this.restService.handleError(error, 'put'); + this.isLoadingResults = false; + }); + }).catch((): void => { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + this.isLoadingResults = false; + }); + } + /** Events handles when mousedown click it will capture the selected node data @private */ + private mouseDown(d: VNFDNODE): void { + event.preventDefault(); + if (d3.event.ctrlKey) { return; } + if (d3.event.shiftKey) { + this.mousedownNode = d; + this.currentSelectedNode = (this.mousedownNode === this.currentSelectedNode) ? null : this.mousedownNode; + this.dragLine.classed('hidden', false) + .attr('d', `M${this.mousedownNode.x},${this.mousedownNode.y}L${this.mousedownNode.x},${this.mousedownNode.y}`); + } + } + /** Event handles when mouseup event occures @private */ + private mouseUp(d: VNFDNODE): void { + if (!this.mousedownNode) { return; } + this.dragLine.classed('hidden', true); + this.mouseupNode = d; + if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'intcp') { + this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKVDUANDINTCP')); + this.deselectPath(); + } + else if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'vdu') { + this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKVDUANDVDU')); + this.deselectPath(); + } + else if (this.mousedownNode.nodeTypeRef === 'intcp' && this.mouseupNode.nodeTypeRef === 'vdu') { + this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKINTCPANDVDU')); + this.deselectPath(); + } + else if (this.mousedownNode.nodeTypeRef === 'cp' && this.mouseupNode.nodeTypeRef === 'intvl') { + this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKCPANDVNFVL')); + this.deselectPath(); + } + else if (this.mousedownNode.nodeTypeRef === 'intvl' && this.mouseupNode.nodeTypeRef === 'cp') { + this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKVNFVLANDCP')); + this.deselectPath(); + } + else if (this.mousedownNode.nodeTypeRef === 'intcp' && this.mouseupNode.nodeTypeRef === 'cp') { + this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKINTCPANDCP')); + this.deselectPath(); + } + else if (this.mousedownNode.nodeTypeRef === 'cp' && this.mouseupNode.nodeTypeRef === 'intcp') { + this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.CANNOTLINKCPANDINTCP')); + this.deselectPath(); + } else if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'cp') { + this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => { + if (vduDetails.id === this.mousedownNode.id) { + if (vduDetails.interface === undefined) { vduDetails.interface = []; } + vduDetails.interface.push({ + 'external-connection-point-ref': this.mouseupNode.id, 'mgmt-interface': true, + name: 'eth_' + this.sharedService.randomString(), + 'virtual-interface': { type: 'VIRTIO' }, + type: 'EXTERNAL' + }); + if (vduDetails['internal-connection-point'] === undefined) { + vduDetails['internal-connection-point'] = []; + } + if (vduDetails['monitoring-param'] === undefined) { + vduDetails['monitoring-param'] = []; + } + if (vduDetails['vm-flavor'] === undefined) { + vduDetails['vm-flavor'] = {}; + } + } + }); + this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails); + this.deselectPath(); + } else if (this.mousedownNode.nodeTypeRef === 'vdu' && this.mouseupNode.nodeTypeRef === 'intvl') { + const setIntCP: string = 'intcp_' + this.sharedService.randomString(); + this.vnfdPackageDetails['internal-vld'].forEach((vldInternal: InternalVLD) => { + if (vldInternal.id === this.mouseupNode.id) { + if (vldInternal['internal-connection-point'] === undefined) { vldInternal['internal-connection-point'] = []; } + vldInternal['internal-connection-point'].push({ 'id-ref': setIntCP }); + } + }); + this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => { + if (vduDetails.id === this.mousedownNode.id) { + if (vduDetails.interface === undefined) { + vduDetails.interface = []; + } + vduDetails.interface.push({ + 'internal-connection-point-ref': setIntCP, name: 'int_' + setIntCP, type: 'INTERNAL', 'virtual-interface': { type: 'VIRTIO' } + }); + if (vduDetails['internal-connection-point'] === undefined) { + vduDetails['internal-connection-point'] = []; + } + vduDetails['internal-connection-point'].push({ + id: setIntCP, name: setIntCP, 'short-name': setIntCP, type: 'VPORT' + }); + } + }); + this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails); + this.deselectPath(); + } + else { + this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.INVALIDSELECTION')); + this.deselectPath(); + } + this.resetMouseActions(); + this.currentSelectedNode = null; + } + /** Events handles when mousemove it will capture the selected node data @private */ + private mousemove(): void { + if (!this.mousedownNode) { return; } + this.dragLine.attr('d', + `M${this.mousedownNode.x},${this.mousedownNode.y}L${d3.mouse(d3.event.currentTarget)[0]},${d3.mouse(d3.event.currentTarget)[1]}`); + } + /** reset Mouse varaibles @private */ + private resetMouseActions(): void { + this.mousedownNode = null; + this.mouseupNode = null; + } + /** Key press event @private */ + private keyDown(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + if (this.lastKeyDown !== -1) { return; } + this.lastKeyDown = d3.event.keyCode; + if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) { + this.gNetwork.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)); + this.gVirutualDeploymentUnit.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)); + this.gConnectionPoint.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)); + this.gIntConnectionPoint.call(d3.drag().on('start', this.dragstarted).on('drag', this.dragged).on('end', this.dragended)); + this.svg.classed('ctrl', true); + } + } + /** Key realse event @private */ + private keyUp(): void { + const graphContainerAttr: GRAPHDETAILS = this.getGraphContainerAttr(); + this.lastKeyDown = -1; + if (d3.event.keyCode === graphContainerAttr.shiftKeyCode) { + this.gNetwork.on('.drag', null); + this.gVirutualDeploymentUnit.on('.drag', null); + this.gConnectionPoint.on('.drag', null); + this.gIntConnectionPoint.on('.drag', null); + this.svg.classed('ctrl', false); + } + } + /** Mosue Drag Line false if it is not satisfied @private */ + private deselectPath(): void { + this.dragLine.classed('hidden', true).attr('d', 'M0,0L0,0'); + } + /** Events handles when Shift Click to perform create cp @private */ + // tslint:disable-next-line: no-any + private singleClick(nodeSelected: any, d: VNFDNODE): void { + this.selectedNode(nodeSelected, d); + } + /** Get confirmation Before Deleting the Node in Topology @private */ + private getDeleteNodeConfirmation(d: VNFDNODE): void { + const modalRef: NgbModalRef = this.modalService.open(ConfirmationTopologyComponent, { backdrop: 'static' }); + modalRef.componentInstance.topologyType = 'Delete'; + modalRef.componentInstance.topologyname = d.name; + if (d.nodeTypeRef === 'vdu') { + modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.VDU'; + } else if (d.nodeTypeRef === 'intvl') { + modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.INTVL'; + } else if (d.nodeTypeRef === 'cp') { + modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.CONNECTIONPOINT'; + } else if (d.nodeTypeRef === 'intcp') { + modalRef.componentInstance.topologytitle = 'PAGE.TOPOLOGY.INTCP'; + } + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.deleteNode(d); + } + }).catch(); + } + /** Delete nodes @private */ + private deleteNode(d: VNFDNODE): void { + const deletedNode: VNFDNODE = d; + this.nodes.forEach((node: VNFDNODE) => { + if (node.id === d.id) { + if (deletedNode.nodeTypeRef === 'cp') { + if (this.vnfdPackageDetails.vdu !== undefined) { + this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => { + if (vduDetails.interface !== undefined) { + const interfacePos: number = vduDetails.interface.map((e: VNFDInterface) => { return e['external-connection-point-ref']; }) + .indexOf(d.id); + if (interfacePos >= 0) { + vduDetails.interface.splice(interfacePos, 1); + } + } + }); + } + const cpPos: number = this.vnfdPackageDetails['connection-point'].map((e: CONNECTIONPOINT) => { return e.name; }) + .indexOf(d.id); + if (cpPos >= 0) { + this.vnfdPackageDetails['connection-point'].splice(cpPos, 1); + } + } else if (deletedNode.nodeTypeRef === 'intcp') { + this.vnfdPackageDetails.vdu.forEach((vduDetails: VDU) => { + // Delete Interface + const interfacePos: number = vduDetails.interface.map((e: VNFDInterface) => { return e['internal-connection-point-ref']; }) + .indexOf(d.id); + if (interfacePos >= 0) { + vduDetails.interface.splice(interfacePos, 1); + } + // Delete Internal CP + const interCPPos: number = vduDetails['internal-connection-point'] + .map((e: VDUInternalConnectionPoint) => { return e.id; }) + .indexOf(d.id); + if (interCPPos >= 0) { + vduDetails['internal-connection-point'].splice(interCPPos, 1); + } + }); + if (this.vnfdPackageDetails['internal-vld'] !== undefined && this.vnfdPackageDetails['internal-vld'].length > 0) { + this.vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => { + const interfacePos: number = internalVLD['internal-connection-point'] + .map((e: VLDInternalConnectionPoint) => { return e['id-ref']; }).indexOf(d.id); + if (interfacePos >= 0) { + internalVLD['internal-connection-point'].splice(interfacePos, 1); + } + }); + } + } else if (deletedNode.nodeTypeRef === 'intvl') { + const intvlPos: number = this.vnfdPackageDetails['internal-vld'] + .map((e: InternalVLD) => { return e.name; }).indexOf(d.id); + if (intvlPos >= 0) { + this.vnfdPackageDetails['internal-vld'].splice(intvlPos, 1); + } + } else if (deletedNode.nodeTypeRef === 'vdu') { + const internalCPList: string[] = []; + if (deletedNode.interface !== undefined && deletedNode.interface.length > 0) { + deletedNode.interface.forEach((interfaceNode: InternalVLD) => { + if (interfaceNode['internal-connection-point-ref'] !== undefined) { + internalCPList.push(interfaceNode['internal-connection-point-ref']); + } + }); + } + internalCPList.forEach((list: string) => { + if (this.vnfdPackageDetails['internal-vld'] !== undefined && this.vnfdPackageDetails['internal-vld'].length > 0) { + this.vnfdPackageDetails['internal-vld'].forEach((internalVLD: InternalVLD) => { + const interfacePos: number = internalVLD['internal-connection-point'] + .map((e: VLDInternalConnectionPoint) => { return e['id-ref']; }).indexOf(list); + if (interfacePos >= 0) { + internalVLD['internal-connection-point'].splice(interfacePos, 1); + } + }); + } + }); + const vduPos: number = this.vnfdPackageDetails.vdu.map((e: VDU) => { return e.id; }).indexOf(d.id); + if (vduPos >= 0) { + this.vnfdPackageDetails.vdu.splice(vduPos, 1); + } + } else { + this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.INVALIDSELECTION')); + } + this.addNodes(environment.VNFPACKAGES_URL, this.identifier, this.vnfdPackageDetails); + } + }); + } + /** Get confirmation before deleting the ink in Topology @private */ + private getDeleteLinkConfirmation(d: Tick): void { + this.notifierService.notify('warning', this.translateService.instant('PAGE.VNFPACKAGE.VNFCOMPOSE.YOUCANNOTDELETELINK')); + } + /** Selected nodes @private */ + // tslint:disable-next-line: no-any + private selectedNode(nodeSeleced: any, d: VNFDNODE): void { + const alreadyIsActive: boolean = nodeSeleced.select('#' + d.id).classed(this.activeNode); + this.deselectAllNodes(); + nodeSeleced.select('#' + d.id).classed(this.activeNode, !alreadyIsActive); + if (d.nodeTypeRef === 'vdu' && !alreadyIsActive) { + this.vnfdPackageDetails.vdu.forEach((res: VDU) => { + if (res.name === d.id) { + this.showRightSideInfo = 'vduInfo'; + this.vduInfo = res; + } + }); + } else if (d.nodeTypeRef === 'intvl' && !alreadyIsActive) { + this.vnfdPackageDetails['internal-vld'].forEach((res: InternalVLD) => { + if (res.name === d.id) { + this.showRightSideInfo = 'intvlInfo'; + this.intvlInfo = res; + this.intvlInfo.shortName = res['short-name']; + this.intvlInfo.ipProfileRef = res['ip-profile-ref']; + } + }); + } else if (d.nodeTypeRef === 'cp' && !alreadyIsActive) { + this.vnfdPackageDetails['connection-point'].forEach((res: CONNECTIONPOINT) => { + if (res.name === d.id) { + this.showRightSideInfo = 'cpInfo'; + this.cpInfo = res; + } + }); + } + else if (d.nodeTypeRef === 'intcp' && !alreadyIsActive) { + this.intcpInfo = d; + this.showRightSideInfo = 'intcpInfo'; + this.intcpInfo.shortName = d['short-name']; + } else { + this.showRightSideInfo = 'vnfdInfo'; + } + } + /** De-select all the selected nodes @private */ + private deselectAllNodes(): void { + this.network.select('image').classed(this.activeNode, false); + this.virutualDeploymentUnit.select('image').classed(this.activeNode, false); + this.connectionPoint.select('image').classed(this.activeNode, false); + this.intConnectionPoint.select('image').classed(this.activeNode, false); + } + /** Events handles when dragstarted @private */ + private dragstarted(d: COMPOSERNODES): void { + d.fx = d.x; + d.fy = d.y; + } + /** Events handles when dragged @private */ + private dragged(d: COMPOSERNODES): void { + d.fx = d.x = d3.event.x; + d.fy = d.y = d3.event.y; + } + /** Events handles when dragended @private */ + private dragended(d: COMPOSERNODES): void { + if (this.forceSimulationActive) { + d.fx = null; + d.fy = null; + } else { + d.fx = d.x; + d.fy = d.y; + this.forceSimulationActive = false; + } + } + /** Events handles when node double click @private */ + private onNodedblClickToggleSidebar(): void { + this.sideBarOpened = false; + } + /** Events handles when node single click @private */ + private onNodeClickToggleSidebar(): void { + this.sideBarOpened = true; + } +} diff --git a/src/app/packages/show-content/ShowContentComponent.html b/src/app/packages/show-content/ShowContentComponent.html new file mode 100644 index 0000000..dbb0935 --- /dev/null +++ b/src/app/packages/show-content/ShowContentComponent.html @@ -0,0 +1,44 @@ + + + + \ No newline at end of file diff --git a/src/app/packages/show-content/ShowContentComponent.scss b/src/app/packages/show-content/ShowContentComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/packages/show-content/ShowContentComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/packages/show-content/ShowContentComponent.ts b/src/app/packages/show-content/ShowContentComponent.ts new file mode 100644 index 0000000..e2431e8 --- /dev/null +++ b/src/app/packages/show-content/ShowContentComponent.ts @@ -0,0 +1,82 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Show content modal + */ +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { ERRORDATA, URLPARAMS } from 'CommonModel'; +import { environment } from 'environment'; +import { RestService } from 'RestService'; + +/** Shows json data in the information modal */ +const defaults: {} = { 'text/json': '' }; + +/** + * Creating component + * @Component takes ShowContentComponent.html as template url + */ +@Component({ + selector: 'app-show-content', + templateUrl: './ShowContentComponent.html', + styleUrls: ['./ShowContentComponent.scss'] +}) +/** Exporting a class @exports ShowContentComponent */ +export class ShowContentComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Contains files information @public */ + public contents: {}[]; + + /** Input contains component objects @public */ + @Input() public params: URLPARAMS; + + /** Instance of the rest service @private */ + private restService: RestService; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate @public + */ + public ngOnInit(): void { + if (this.params.page === 'nsd') { + this.restService.getResource(environment.NSDESCRIPTORS_URL + '/' + this.params.id + '/artifacts/artifactPath') + .subscribe((nsd: {}[]) => { + this.contents = nsd; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } else if (this.params.page === 'vnfd') { + this.restService.getResource(environment.VNFPACKAGES_URL + '/' + this.params.id + '/artifacts/artifactPath') + .subscribe((vnfd: {}[]) => { + this.contents = vnfd; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + }); + } + } +} diff --git a/src/app/packages/vnf-packages/VNFPackagesComponent.html b/src/app/packages/vnf-packages/VNFPackagesComponent.html new file mode 100644 index 0000000..2d7298d --- /dev/null +++ b/src/app/packages/vnf-packages/VNFPackagesComponent.html @@ -0,0 +1,44 @@ + +
+
VNF {{'PACKAGES' | translate}}
+ + + +
+
+
+ +
+
+ {{'DROPFILES' | translate}}
+
+
+
+
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/packages/vnf-packages/VNFPackagesComponent.scss b/src/app/packages/vnf-packages/VNFPackagesComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/packages/vnf-packages/VNFPackagesComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/packages/vnf-packages/VNFPackagesComponent.ts b/src/app/packages/vnf-packages/VNFPackagesComponent.ts new file mode 100644 index 0000000..13707e1 --- /dev/null +++ b/src/app/packages/vnf-packages/VNFPackagesComponent.ts @@ -0,0 +1,275 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file VNF Package details Component. + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA } from 'CommonModel'; +import { ComposePackages } from 'ComposePackages'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { LocalDataSource } from 'ng2-smart-table'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; +import { VNFData, VNFDDetails } from 'VNFDModel'; +import { VNFPackagesActionComponent } from 'VNFPackagesAction'; + +/** + * Creating component + * @Component takes VNFPackagesComponent.html as template url + */ +@Component({ + selector: 'app-vnf-packages', + templateUrl: './VNFPackagesComponent.html', + styleUrls: ['./VNFPackagesComponent.scss'] +}) +/** Exporting a class @exports VNFPackagesComponent */ +export class VNFPackagesComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Data of smarttable populate through LocalDataSource @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** handle translate @public */ + public translateService: TranslateService; + + /** Columns list of the smart table @public */ + public columnLists: object = {}; + + /** Settings for smarttable to populate the table with columns @public */ + public settings: object = {}; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** Element ref for fileInput @public */ + @ViewChild('fileInput', { static: true }) public fileInput: ElementRef; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Formation of appropriate Data for LocalDatasource @private */ + private vnfData: VNFData[] = []; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** variables contains file information */ + private fileData: string | ArrayBuffer; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.translateService = this.injector.get(TranslateService); + this.notifierService = this.injector.get(NotifierService); + this.modalService = this.injector.get(NgbModal); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.generateColumns(); + this.generateSettings(); + this.generateData(); + this.headers = new HttpHeaders({ + 'Content-Type': 'application/gzip', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** smart table Header Colums @public */ + public generateColumns(): void { + this.columnLists = { + shortName: { title: this.translateService.instant('SHORTNAME'), width: '15%', sortDirection: 'asc' }, + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' }, + type: { + title: this.translateService.instant('TYPE'), + filter: { + type: 'list', + config: { + selectText: 'Select', + list: [ + { value: 'vnfd', title: 'VNF' }, + { value: 'pnfd', title: 'PNF' }, + { value: 'hnfd', title: 'HNF' } + ] + } + }, + width: '10%' + }, + description: { title: this.translateService.instant('DESCRIPTION'), width: '20%' }, + vendor: { title: this.translateService.instant('VENDOR'), width: '10%' }, + version: { title: this.translateService.instant('VERSION'), width: '10%' }, + Actions: { + name: 'Action', width: '15%', filter: false, sort: false, type: 'custom', + title: this.translateService.instant('ACTIONS'), + valuePrepareFunction: (cell: VNFData, row: VNFData): VNFData => row, renderComponent: VNFPackagesActionComponent + } + }; + } + + /** smart table Data Settings @public */ + public generateSettings(): void { + this.settings = { + edit: { + editButtonContent: '', + confirmSave: true + }, + delete: { + deleteButtonContent: '', + confirmDelete: true + }, + columns: this.columnLists, + actions: { + add: false, + edit: false, + delete: false, + position: 'right' + }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** smart table listing manipulation @public */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** OnUserRowSelect Function @public */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'vnf-package' }); + this.dataService.changeMessage(event.data); + } + + /** Drag and drop feature and fetchind the details of files @public */ + public filesDropped(files: FileList): void { + if (files && files.length === 1) { + this.isLoadingResults = true; + this.sharedService.getFileString(files, 'gz').then((fileContent: ArrayBuffer): void => { + const apiURLHeader: APIURLHEADER = { + url: environment.VNFPACKAGESCONTENT_URL, + httpOptions: { headers: this.headers } + }; + this.saveFileData(apiURLHeader, fileContent); + }).catch((err: string): void => { + this.isLoadingResults = false; + if (err === 'typeError') { + this.notifierService.notify('error', this.translateService.instant('GZFILETYPEERRROR')); + } else { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + }); + } else if (files && files.length > 1) { + this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION')); + } + } + + /** Post the droped files and reload the page @public */ + public saveFileData(urlHeader: APIURLHEADER, fileData: {}): void { + this.fileInput.nativeElement.value = null; + this.restService.postResource(urlHeader, fileData).subscribe((result: {}) => { + this.notifierService.notify('success', this.translateService.instant('PAGE.VNFPACKAGE.CREATEDSUCCESSFULLY')); + this.generateData(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + this.isLoadingResults = false; + }); + } + + /** Generate nsData object from loop and return for the datasource @public */ + public generatevnfdData(vnfdpackagedata: VNFDDetails): VNFData { + return { + shortName: vnfdpackagedata['short-name'], + identifier: vnfdpackagedata._id, + type: vnfdpackagedata._admin.type, + description: vnfdpackagedata.description, + vendor: vnfdpackagedata.vendor, + version: vnfdpackagedata.version + }; + } + /** Handle compose new ns package method @public */ + public composeVNFPackage(): void { + this.modalService.open(ComposePackages, { backdrop: 'static' }).componentInstance.params = { page: 'vnf-package' }; + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } + + /** Fetching the data from server to Load in the smarttable @protected */ + protected generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.VNFPACKAGESCONTENT_URL).subscribe((vnfdPackageData: VNFDDetails[]) => { + this.vnfData = []; + vnfdPackageData.forEach((vnfdpackagedata: VNFDDetails) => { + const vnfDataObj: VNFData = this.generatevnfdData(vnfdpackagedata); + this.vnfData.push(vnfDataObj); + }); + if (this.vnfData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.vnfData).then((data: boolean) => { + this.isLoadingResults = false; + }).catch(() => { + this.isLoadingResults = false; + }); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } +} diff --git a/src/app/page-not-found/PageNotFoundComponent.html b/src/app/page-not-found/PageNotFoundComponent.html new file mode 100644 index 0000000..f546e3d --- /dev/null +++ b/src/app/page-not-found/PageNotFoundComponent.html @@ -0,0 +1,27 @@ + +
+ +

{{'PAGENOTFOUND.OOPS' | translate}}

+ + {{'PAGENOTFOUND.CONTENT' | translate}} + + + {{'PAGENOTFOUND.MEAN' | translate}} {{'PAGE.DASHBOARD.DASHBOARD' | translate}} + +
\ No newline at end of file diff --git a/src/app/page-not-found/PageNotFoundComponent.scss b/src/app/page-not-found/PageNotFoundComponent.scss new file mode 100644 index 0000000..0e81f2b --- /dev/null +++ b/src/app/page-not-found/PageNotFoundComponent.scss @@ -0,0 +1,29 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +@import "../../assets/scss/mixins/mixin"; +@import "../../assets/scss/variable"; +.page-not-found { + @include padding-percentage-value(10rem, 10rem, 10rem, 10rem); + .four-not-four { + @include font(null, 5rem, bold); + } + a:hover { + color: $primary; + text-decoration: underline; + } +} \ No newline at end of file diff --git a/src/app/page-not-found/PageNotFoundComponent.ts b/src/app/page-not-found/PageNotFoundComponent.ts new file mode 100644 index 0000000..ef515ec --- /dev/null +++ b/src/app/page-not-found/PageNotFoundComponent.ts @@ -0,0 +1,42 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Page not found component + */ +import { Component, OnInit } from '@angular/core'; + +/** + * Creating component + * @Component takes PageNotFoundComponent.html as template url + */ +@Component({ + selector: 'app-page-not-found', + templateUrl: './PageNotFoundComponent.html', + styleUrls: ['./PageNotFoundComponent.scss'] +}) +/** Exporting a class @exports PageNotFoundComponent */ +export class PageNotFoundComponent implements OnInit { + constructor() { + //donothing + } + + public ngOnInit(): void { + //donothing + } + +} diff --git a/src/app/projects/ProjectsComponent.html b/src/app/projects/ProjectsComponent.html new file mode 100644 index 0000000..ec15861 --- /dev/null +++ b/src/app/projects/ProjectsComponent.html @@ -0,0 +1,35 @@ + +
+
{{'PAGE.DASHBOARD.PROJECTS' | translate}}
+ + + +
+
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/projects/ProjectsComponent.scss b/src/app/projects/ProjectsComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/projects/ProjectsComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/projects/ProjectsComponent.ts b/src/app/projects/ProjectsComponent.ts new file mode 100644 index 0000000..7524994 --- /dev/null +++ b/src/app/projects/ProjectsComponent.ts @@ -0,0 +1,203 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Project details Component. + */ +import { Component, Injector, OnDestroy, OnInit } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { LocalDataSource } from 'ng2-smart-table'; +import { ProjectCreateUpdateComponent } from 'ProjectCreateUpdate'; +import { ProjectLinkComponent } from 'ProjectLinkComponent'; +import { ProjectData, ProjectDetails } from 'ProjectModel'; +import { ProjectsActionComponent } from 'ProjectsAction'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes ProjectsComponent.html as template url + */ +@Component({ + selector: 'app-projects', + templateUrl: './ProjectsComponent.html', + styleUrls: ['./ProjectsComponent.scss'] +}) +/** Exporting a class @exports ProjectsComponent */ +export class ProjectsComponent implements OnInit, OnDestroy { + /** To inject services @public */ + public injector: Injector; + + /** handle translate @public */ + public translateService: TranslateService; + + /** Data of smarttable populate through LocalDataSource @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** Columns list of the smart table @public */ + public columnLists: object = {}; + + /** Settings for smarttable to populate the table with columns @public */ + public settings: object = {}; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Formation of appropriate Data for LocalDatasource @private */ + private projectData: ProjectData[] = []; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.modalService = this.injector.get(NgbModal); + this.translateService = this.injector.get(TranslateService); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.generateColumns(); + this.generateSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** smart table Header Colums @public */ + public generateColumns(): void { + this.columnLists = { + projectName: { + title: this.translateService.instant('NAME'), width: '55%', sortDirection: 'asc', type: 'custom', + valuePrepareFunction: (cell: ProjectData, row: ProjectData): ProjectData => row, + renderComponent: ProjectLinkComponent + }, + modificationDate: { title: this.translateService.instant('MODIFIED'), width: '20%' }, + creationDate: { title: this.translateService.instant('CREATED'), width: '20%' }, + Actions: { + name: 'Action', width: '5%', filter: false, sort: false, type: 'custom', + title: this.translateService.instant('ACTIONS'), + valuePrepareFunction: (cell: ProjectData, row: ProjectData): ProjectData => row, + renderComponent: ProjectsActionComponent + } + }; + } + + /** smart table Data Settings @public */ + public generateSettings(): void { + this.settings = { + edit: { + editButtonContent: '', + confirmSave: true + }, + delete: { + deleteButtonContent: '', + confirmDelete: true + }, + columns: this.columnLists, + actions: { + add: false, + edit: false, + delete: false, + position: 'right' + }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** Modal service to initiate the project add @private */ + public projectAdd(): void { + const modalRef: NgbModalRef = this.modalService.open(ProjectCreateUpdateComponent, { backdrop: 'static' }); + modalRef.componentInstance.projectType = 'Add'; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.generateData(); + } + }).catch(); + } + + /** smart table listing manipulation @private */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** convert UserRowSelect Function @private */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'projects' }); + this.dataService.changeMessage(event.data); + } + + /** Generate Projects object from loop and return for the datasource @public */ + public generateProjectData(projectData: ProjectDetails): ProjectData { + return { + projectName: projectData.name, + modificationDate: this.sharedService.convertEpochTime(projectData._admin.modified), + creationDate: this.sharedService.convertEpochTime(projectData._admin.created), + id: projectData._id, + project: projectData._id + }; + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } + + /** Fetching the data from server to Load in the smarttable @protected */ + protected generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.PROJECTS_URL).subscribe((projectsData: ProjectDetails[]) => { + this.projectData = []; + projectsData.forEach((projectData: ProjectDetails) => { + const projectDataObj: ProjectData = this.generateProjectData(projectData); + this.projectData.push(projectDataObj); + }); + this.dataSource.load(this.projectData).then((data: boolean) => { + this.isLoadingResults = false; + }).catch(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } +} diff --git a/src/app/projects/ProjectsModule.ts b/src/app/projects/ProjectsModule.ts new file mode 100644 index 0000000..16147b9 --- /dev/null +++ b/src/app/projects/ProjectsModule.ts @@ -0,0 +1,86 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file SDN Controller module. + */ +import { CommonModule } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; +import { NgModule } from '@angular/core'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { FormsModule } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterModule, Routes } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { TranslateModule } from '@ngx-translate/core'; +import { DataService } from 'DataService'; +import { LoaderModule } from 'LoaderModule'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { PagePerRowModule } from 'PagePerRowModule'; +import { PageReloadModule } from 'PageReloadModule'; +import { ProjectCreateUpdateComponent } from 'ProjectCreateUpdate'; +import { ProjectsComponent } from 'ProjectsComponent'; + +/** + * configures routers + */ +const routes: Routes = [ + { + path: '', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: null }] + }, + component: ProjectsComponent + } +]; + +/** + * Creating @NgModule component for Modules + */ +// tslint:disable-next-line: no-stateless-class +@NgModule({ + imports: [ + FormsModule, + CommonModule, + HttpClientModule, + Ng2SmartTableModule, + FlexLayoutModule, + TranslateModule, + RouterModule.forChild(routes), + NgbModule, + PagePerRowModule, + ReactiveFormsModule, + LoaderModule, + PageReloadModule, + NgSelectModule + ], + declarations: [ + ProjectsComponent, + ProjectCreateUpdateComponent + ], + providers: [ + DataService + ], + entryComponents: [ + ProjectCreateUpdateComponent + ] +}) +/** Exporting a class @exports ProjectsModule */ +export class ProjectsModule { + // empty module +} diff --git a/src/app/projects/project-create-update/ProjectCreateUpdateComponent.html b/src/app/projects/project-create-update/ProjectCreateUpdateComponent.html new file mode 100644 index 0000000..c327119 --- /dev/null +++ b/src/app/projects/project-create-update/ProjectCreateUpdateComponent.html @@ -0,0 +1,51 @@ + +
+ + + +
+ diff --git a/src/app/projects/project-create-update/ProjectCreateUpdateComponent.scss b/src/app/projects/project-create-update/ProjectCreateUpdateComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/projects/project-create-update/ProjectCreateUpdateComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/projects/project-create-update/ProjectCreateUpdateComponent.ts b/src/app/projects/project-create-update/ProjectCreateUpdateComponent.ts new file mode 100644 index 0000000..ea0bb8a --- /dev/null +++ b/src/app/projects/project-create-update/ProjectCreateUpdateComponent.ts @@ -0,0 +1,233 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Project Add Modal + */ +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { ProjectData, ProjectDetails } from 'ProjectModel'; +import { ProjectService } from 'ProjectService'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; + +/** + * Creating component + * @Component takes ProjectCreateUpdateComponent.html as template url + */ +@Component({ + selector: 'app-project-create-update', + templateUrl: './ProjectCreateUpdateComponent.html', + styleUrls: ['./ProjectCreateUpdateComponent.scss'] +}) +/** Exporting a class @exports ProjectCreateUpdateComponent */ +export class ProjectCreateUpdateComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Instance of the rest service @public */ + public restService: RestService; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Contains the recently created project details @public */ + public recentProject: ProjectDetails; + + /** Contains project name @public */ + public projectName: string; + + /** Contains project create or edit @public */ + public getProjectType: string; + + /** To inject input type services @public */ + @Input() public projectType: string; + + /** FormGroup user Edit Account added to the form @ html @public */ + public projectForm: FormGroup; + + /** Form submission Add */ + public submitted: boolean = false; + + /** Check the loading results for loader status @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Holds list of domains @public */ + public domains: {}[] = []; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** DataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Contains project name ref @private */ + private projectRef: string; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** ModalData instance of modal @private */ + private modalData: MODALCLOSERESPONSEDATA; + + /** Holds all project details @private */ + private projectService: ProjectService; + + constructor(injector: Injector) { + this.injector = injector; + this.formBuilder = this.injector.get(FormBuilder); + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + this.dataService = this.injector.get(DataService); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + this.projectService = this.injector.get(ProjectService); + /** Initializing Form Action */ + this.projectForm = this.formBuilder.group({ + project_name: ['', Validators.required], + domain_name: [null] + }); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.projectForm.controls; } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.getProjectType = this.projectType; + if (this.getProjectType === 'Edit') { + this.dataService.currentMessage.subscribe((data: ProjectData) => { + if (data.projectName !== undefined || data.projectName !== '' || data.projectName !== null) { + this.projectName = data.projectName; + this.projectRef = data.id; + } + }); + } else { + this.getProjects(); + } + } + + /** Get the last project name @public */ + public getProjects(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.PROJECTS_URL).subscribe((projects: ProjectDetails[]) => { + this.recentProject = projects.slice(-1).pop(); + this.getDomainName(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** On modal submit users acction will called @public */ + public projectAction(userType: string): void { + this.submitted = true; + this.modalData = { + message: 'Done' + }; + this.sharedService.cleanForm(this.projectForm); + if (!this.projectForm.invalid) { + if (userType === 'Add') { + this.createProject(); + } else if (userType === 'Edit') { + this.editProject(); + } + } + } + + /** Create project @public */ + public createProject(): void { + this.isLoadingResults = true; + const apiURLHeader: APIURLHEADER = { + url: environment.PROJECTS_URL + }; + const projectPayload: {} = { + name: this.projectForm.value.project_name, + domain_name: !isNullOrUndefined(this.projectForm.value.domain_name) ? this.projectForm.value.domain_name : undefined + }; + this.restService.postResource(apiURLHeader, projectPayload).subscribe(() => { + this.activeModal.close(this.modalData); + this.isLoadingResults = false; + this.notifierService.notify('success', this.translateService.instant('PAGE.PROJECT.CREATEDSUCCESSFULLY')); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + this.isLoadingResults = false; + }); + } + /** Edit project @public */ + public editProject(): void { + this.isLoadingResults = true; + const apiURLHeader: APIURLHEADER = { + url: environment.PROJECTS_URL + '/' + this.projectRef + }; + this.restService.patchResource(apiURLHeader, { name: this.projectForm.value.project_name }).subscribe(() => { + this.activeModal.close(this.modalData); + this.isLoadingResults = false; + this.projectService.setHeaderProjects(); + this.notifierService.notify('success', this.translateService.instant('PAGE.PROJECT.UPDATEDSUCCESSFULLY')); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'patch'); + this.isLoadingResults = false; + }); + } + /** Get domain name @private */ + private getDomainName(): void { + this.restService.getResource(environment.DOMAIN_URL).subscribe((domains: { project_domain_name: string, user_domain_name: string }) => { + let domainNames: string[] = []; + if (!isNullOrUndefined(domains.project_domain_name)) { + domainNames = domainNames.concat(domains.project_domain_name.split(',')); + } + if (!isNullOrUndefined(domains.user_domain_name)) { + domainNames = domainNames.concat(domains.user_domain_name.split(',')); + } + domainNames = Array.from(new Set(domainNames)); + this.checkDomainNames(domainNames); + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** Check the domain names and create modal for domain select @private */ + private checkDomainNames(domainNames: string[]): void { + if (domainNames.length > 0) { + domainNames.forEach((domainName: string) => { + if (!domainName.endsWith(':ro')) { + this.domains.push({ id: domainName, text: domainName }); + } + }); + } + } +} diff --git a/src/app/roles/RolesComponent.html b/src/app/roles/RolesComponent.html new file mode 100644 index 0000000..da59906 --- /dev/null +++ b/src/app/roles/RolesComponent.html @@ -0,0 +1,18 @@ + + \ No newline at end of file diff --git a/src/app/roles/RolesComponent.scss b/src/app/roles/RolesComponent.scss new file mode 100644 index 0000000..f6cb0fe --- /dev/null +++ b/src/app/roles/RolesComponent.scss @@ -0,0 +1,18 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ + diff --git a/src/app/roles/RolesComponent.ts b/src/app/roles/RolesComponent.ts new file mode 100644 index 0000000..82f20b4 --- /dev/null +++ b/src/app/roles/RolesComponent.ts @@ -0,0 +1,58 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Roles component. + */ +import { Component, Injector } from '@angular/core'; +import { Router, RouterEvent } from '@angular/router'; + +/** + * Creating component + * @Component takes RolesComponent.html as template url + */ +@Component({ + selector: 'app-roles', + templateUrl: './RolesComponent.html', + styleUrls: ['./RolesComponent.scss'] +}) + +/** Exporting a class @exports RolesComponent */ +export class RolesComponent { + /** Invoke service injectors @public */ + public injector: Injector; + + /** Holds teh instance of router service @private */ + private router: Router; + + // creates role datails component + constructor(injector: Injector) { + this.injector = injector; + this.router = this.injector.get(Router); + this.router.events.subscribe((event: RouterEvent) => { + this.redirectToList(event.url); + }); + } + + /** Return to role datails list */ + public redirectToList(getURL: string): void { + if (getURL === '/roles') { + this.router.navigate(['/roles/details']).catch(); + } + } + +} diff --git a/src/app/roles/RolesModule.ts b/src/app/roles/RolesModule.ts new file mode 100644 index 0000000..88e1225 --- /dev/null +++ b/src/app/roles/RolesModule.ts @@ -0,0 +1,87 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ + +/** + * @file Roles Module + */ +import { CommonModule } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterModule, Routes } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateModule } from '@ngx-translate/core'; +import { LoaderModule } from 'LoaderModule'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { PagePerRowModule } from 'PagePerRowModule'; +import { PageReloadModule } from 'PageReloadModule'; +import { RolesComponent } from 'Roles'; +import { RolesActionComponent } from 'RolesAction'; +import { RolesCreateEditComponent } from 'RolesCreateEdit'; +import { RolesDetailsComponent } from 'RolesDetails'; + +/** const values for Roles Routes */ +const routes: Routes = [ + { + path: '', + component: RolesComponent, + children: [ + { + path: 'details', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'ROLES', url: null }] + }, + component: RolesDetailsComponent + }, + { + path: 'create', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'ROLES', url: '/roles/details' }, + { title: 'PAGE.ROLES.CREATEROLE', url: null }] + }, + component: RolesCreateEditComponent + }, + { + path: 'edit/:id', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'ROLES', url: '/roles/details' }, + { title: '{id}', url: null }] + }, + component: RolesCreateEditComponent + } + ] + } +]; +/** + * An NgModule is a class adorned with the @NgModule decorator function. + * @NgModule takes a metadata object that tells Angular how to compile and run module code. + */ +@NgModule({ + imports: [FormsModule, ReactiveFormsModule, CommonModule, HttpClientModule, TranslateModule, + RouterModule.forChild(routes), NgbModule, PagePerRowModule, Ng2SmartTableModule, LoaderModule, PageReloadModule], + declarations: [RolesComponent, RolesDetailsComponent, RolesActionComponent, RolesCreateEditComponent], + entryComponents: [RolesActionComponent] +}) +/** + * AppModule is the Root module for application + */ +export class RolesModule { + /** Variables declared to avoid state-less class */ + private rolesModule: string; +} diff --git a/src/app/roles/roles-create-edit/RolesCreateEditComponent.html b/src/app/roles/roles-create-edit/RolesCreateEditComponent.html new file mode 100644 index 0000000..7bb2a1f --- /dev/null +++ b/src/app/roles/roles-create-edit/RolesCreateEditComponent.html @@ -0,0 +1,92 @@ + +
+
+ {{ (getRoleType == 'Add' ? 'PAGE.ROLES.CREATEROLE' : 'PAGE.ROLES.EDITROLE') | translate}}
+ + + +
+
+
+ + +
+ +
+
+
+ +
+ +
+
+
+
+ + +
+
+
+ + + +
+ +
+
+ +
+
+
+
{{permission.operation}}
+
+ + + +
+
+
+
+
+
+
+
+ + +
+ \ No newline at end of file diff --git a/src/app/roles/roles-create-edit/RolesCreateEditComponent.scss b/src/app/roles/roles-create-edit/RolesCreateEditComponent.scss new file mode 100644 index 0000000..362973a --- /dev/null +++ b/src/app/roles/roles-create-edit/RolesCreateEditComponent.scss @@ -0,0 +1,51 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +@import '../../../assets/scss/mixins/mixin'; +@import '../../../assets/scss/variable'; + .custom-button{ + label { + @include padding-value(2,2,2,2); + &.active { + @include background(null, $white, null, null, null); + span{ + img { + opacity: 1; + } + } + } + input[type="radio"] { + display: none; + } + span { + display: inline-block; + @include wh-value(30px,30px); + cursor: pointer; + border-radius: 50%; + @include border(all,2,solid, $white); + @include box-shadow(0px, 1px, 3px, 0px, $dark); + @include background(null, null, null, no-repeat, center); + text-align: center; + @include line-height(25px); + img { + width:50%; + opacity: 0; + @include transition(all, 0.3, null, ease); + } + } + } +} \ No newline at end of file diff --git a/src/app/roles/roles-create-edit/RolesCreateEditComponent.ts b/src/app/roles/roles-create-edit/RolesCreateEditComponent.ts new file mode 100644 index 0000000..0419c5f --- /dev/null +++ b/src/app/roles/roles-create-edit/RolesCreateEditComponent.ts @@ -0,0 +1,335 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Roles Create and Edit Component + */ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA } from 'CommonModel'; +import { environment } from 'environment'; +import * as jsonpath from 'jsonpath'; +import { RestService } from 'RestService'; +import { Permission, PermissionGroup, RoleConfig, RoleData } from 'RolesModel'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; + +/** + * Creating component + * @Component takes RolesCreateEditComponent.html as template url + */ +@Component({ + selector: 'app-roles-create-edit', + templateUrl: './RolesCreateEditComponent.html', + styleUrls: ['./RolesCreateEditComponent.scss'] +}) +/** Exporting a class @exports RolesCreateEditComponent */ +export class RolesCreateEditComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** contains role permissions from config file @public */ + public rolePermissions: {}[]; + + /** contains role form group information @public */ + public roleForm: FormGroup; + + /** Instance of the rest service @public */ + public restService: RestService; + + /** Check the loading results for loader status @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Instance to check role form submitted status @public */ + public submitted: boolean = false; + + /** Contains role create or edit value @public */ + public getRoleType: string; + + /** Contains view selection value either text or preview @public */ + public viewMode: string = 'text'; + + /** Contains role id value @private */ + private roleRef: string; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** contians form builder module @private */ + private formBuilder: FormBuilder; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Varaibles to hold http client @private */ + private httpClient: HttpClient; + + /** Holds the instance of activatedRoute of router service @private */ + private activatedRoute: ActivatedRoute; + + /** Holds the instance of roter service @private */ + private router: Router; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.formBuilder = this.injector.get(FormBuilder); + this.sharedService = this.injector.get(SharedService); + this.httpClient = this.injector.get(HttpClient); + this.activatedRoute = this.injector.get(ActivatedRoute); + this.router = this.injector.get(Router); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.headers = new HttpHeaders({ + 'Content-Type': 'application/json', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + this.roleForm = this.formBuilder.group({ + roleName: ['', [Validators.required]], + permissions: [''] + }); + this.getRolePermissions(); + } + + /** Get role permission information based on action @public */ + public getRolePermissions(): void { + this.isLoadingResults = true; + this.loadPermissions().then((response: RoleConfig) => { + this.rolePermissions = response.rolePermissions; + if (this.activatedRoute.snapshot.url[0].path === 'create') { + this.getRoleType = 'Add'; + this.isLoadingResults = false; + } else { + this.getRoleType = 'Edit'; + this.getRoleData(); + } + }).catch(() => { + // Empty Block + }); + } + + /** Check Role Create or Edit and proceed action @public */ + public roleCheck(): void { + this.submitted = true; + if (!this.roleForm.valid) { + const errorIp: Element = document.querySelector('.ng-invalid[formControlName]'); + if (errorIp) { + errorIp.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + } else { + if (this.getRoleType === 'Add') { + this.createRole(); + } else { + this.editRole(); + } + } + } + + /** Convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.roleForm.controls; } + + /** Create role @private */ + public createRole(): void { + const apiURLHeader: APIURLHEADER = { + url: environment.ROLES_URL, + httpOptions: { headers: this.headers } + }; + let permissionData: Object = {}; + this.sharedService.cleanForm(this.roleForm); + if (!this.roleForm.invalid) { + if (this.viewMode === 'preview') { + this.isLoadingResults = true; + permissionData = this.generatePermissions(); + this.roleCreateAPI(apiURLHeader, permissionData); + } else { + if (this.checkPermission()) { + this.isLoadingResults = true; + permissionData = this.roleForm.value.permissions !== '' ? JSON.parse(this.roleForm.value.permissions) : {}; + this.roleCreateAPI(apiURLHeader, permissionData); + } + } + } + } + + /** Method to handle role create API call @public */ + public roleCreateAPI(apiURLHeader: APIURLHEADER, permissionData: {}): void { + const postData: {} = { + name: this.roleForm.value.roleName, + permissions: !isNullOrUndefined(permissionData) ? permissionData : {} + }; + this.restService.postResource(apiURLHeader, postData).subscribe(() => { + this.notifierService.notify('success', this.translateService.instant('PAGE.ROLES.CREATEDSUCCESSFULLY')); + this.router.navigate(['/roles/details']).catch(); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'post'); + }); + } + + /** Edit role @private */ + public editRole(): void { + const apiURLHeader: APIURLHEADER = { + url: environment.ROLES_URL + '/' + this.roleRef, + httpOptions: { headers: this.headers } + }; + let permissionData: Object = {}; + this.sharedService.cleanForm(this.roleForm); + if (!this.roleForm.invalid) { + if (this.viewMode === 'preview') { + this.isLoadingResults = true; + permissionData = this.generatePermissions(); + this.roleEditAPI(apiURLHeader, permissionData); + } else { + if (this.checkPermission()) { + this.isLoadingResults = true; + permissionData = this.roleForm.value.permissions !== '' ? JSON.parse(this.roleForm.value.permissions) : {}; + this.roleEditAPI(apiURLHeader, permissionData); + } + } + } + } + + /** Method to handle role edit API call */ + public roleEditAPI(apiURLHeader: APIURLHEADER, permissionData: {}): void { + const postData: {} = { + name: this.roleForm.value.roleName, + permissions: !isNullOrUndefined(permissionData) ? permissionData : {} + }; + this.restService.patchResource(apiURLHeader, postData).subscribe(() => { + this.notifierService.notify('success', this.translateService.instant('PAGE.ROLES.UPDATEDSUCCESSFULLY')); + this.router.navigate(['/roles/details']).catch(); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'patch'); + }); + } + + /** Method to handle toggle role view selection @public */ + public viewSelection(): void { + if (this.viewMode === 'text' && this.checkPermission()) { + this.loadPermissions().then((response: RoleConfig) => { + this.rolePermissions = response.rolePermissions; + const permissions: {} = this.roleForm.value.permissions !== '' ? JSON.parse(this.roleForm.value.permissions) : {}; + this.filterRoleData(permissions); + this.viewMode = 'preview'; + }).catch(() => { + // Empty Block + }); + } else { + const rolePermission: {} = this.generatePermissions(); + if (Object.keys(rolePermission).length !== 0) { + this.roleForm.patchValue({ permissions: JSON.stringify(rolePermission, null, '\t') }); + } + this.viewMode = 'text'; + } + + } + + /** Generate role permission post data @private */ + private generatePermissions(): Object { + const permissions: Object = {}; + this.rolePermissions.forEach((permissionGroup: PermissionGroup) => { + if (!isNullOrUndefined(permissionGroup)) { + permissionGroup.permissions.forEach((permission: Permission) => { + if (!isNullOrUndefined(permission.value) && permission.value !== 'NA') { + permissions[permission.operation] = permission.value; + } + }); + } + }); + return permissions; + } + + /** Validation method for check permission json string @private */ + private checkPermission(): boolean { + if (this.roleForm.value.permissions) { + if (!this.sharedService.checkJson(this.roleForm.value.permissions)) { + this.notifierService.notify('error', this.translateService.instant('PAGE.ROLES.ROLEJSONERROR')); + return false; + } else { + this.roleForm.value.permissions = this.roleForm.value.permissions.replace(/'/g, '"'); + const rolePermission: {} = JSON.parse(this.roleForm.value.permissions); + for (const key of Object.keys(rolePermission)) { + if (typeof rolePermission[key] !== 'boolean') { + this.notifierService.notify('error', this.translateService.instant('PAGE.ROLES.ROLEKEYERROR', { roleKey: key })); + return false; + } + } + } + } + return true; + } + + /** Get role data from NBI based on ID and apply filter @private */ + private getRoleData(): void { + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.roleRef = this.activatedRoute.snapshot.paramMap.get('id'); + if (!isNullOrUndefined(this.roleRef)) { + this.restService.getResource(environment.ROLES_URL + '/' + this.roleRef).subscribe((data: RoleData) => { + this.roleForm.setValue({ roleName: data.name, permissions: JSON.stringify(data.permissions, null, '\t') }); + this.filterRoleData(data.permissions); + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.router.navigate(['/roles/details']).catch(); + this.restService.handleError(error, 'get'); + }); + } + } + + /** Method to filter role data @private */ + private filterRoleData(permissions: {}): void { + Object.keys(permissions).forEach((permission: string) => { + jsonpath.apply(this.rolePermissions, '$..permissions[?(@.operation == "' + permission + '")]', (response: Permission) => { + if (response.operation === permission) { + response.value = permissions[permission]; + } + return response; + }); + }); + } + + /** Method to load the role permission Json file @private */ + private async loadPermissions(): Promise { + const jsonFile: string = environment.PERMISSIONS_CONFIG_FILE + '?cb=' + new Date().getTime(); + return new Promise((resolve: Function, reject: Function): void => { + this.httpClient.get(jsonFile).subscribe((response: Response) => { + resolve(response); + }, (error: {}) => { + reject(); + }); + }); + } + +} diff --git a/src/app/roles/roles-details/RolesDetailsComponent.html b/src/app/roles/roles-details/RolesDetailsComponent.html new file mode 100644 index 0000000..28a3fe7 --- /dev/null +++ b/src/app/roles/roles-details/RolesDetailsComponent.html @@ -0,0 +1,36 @@ + +
+
{{'ROLES' | translate}}
+ + + +
+
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/roles/roles-details/RolesDetailsComponent.scss b/src/app/roles/roles-details/RolesDetailsComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/roles/roles-details/RolesDetailsComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/roles/roles-details/RolesDetailsComponent.ts b/src/app/roles/roles-details/RolesDetailsComponent.ts new file mode 100644 index 0000000..2568f5a --- /dev/null +++ b/src/app/roles/roles-details/RolesDetailsComponent.ts @@ -0,0 +1,191 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Roles Deatils component. + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { ERRORDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { LocalDataSource } from 'ng2-smart-table'; +import { RestService } from 'RestService'; +import { RolesActionComponent } from 'RolesAction'; +import { RoleData, RoleDetails } from 'RolesModel'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes RolesComponent.html as template url + */ +@Component({ + selector: 'app-roles-details', + templateUrl: './RolesDetailsComponent.html', + styleUrls: ['./RolesDetailsComponent.scss'] +}) +export class RolesDetailsComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Formation of appropriate Data for LocalDatasource @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** handle translate @public */ + public translateService: TranslateService; + + /** Columns list of the smart table @public */ + public columnLists: object = {}; + + /** Settings for smarttable to populate the table with columns @public */ + public settings: object = {}; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Contains role details data @private */ + private roleData: RoleData[] = []; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + /** Holds the instance of roter service @private */ + private router: Router; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.translateService = this.injector.get(TranslateService); + this.router = this.injector.get(Router); + } + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.generateColumns(); + this.generateSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + /** smart table Header Colums @public */ + public generateColumns(): void { + this.columnLists = { + name: { title: this.translateService.instant('NAME'), width: '30%', sortDirection: 'asc' }, + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '35%' }, + modified: { title: this.translateService.instant('MODIFIED'), width: '15%' }, + created: { title: this.translateService.instant('CREATED'), width: '15%' }, + Actions: { + name: 'Actions', width: '5%', filter: false, sort: false, type: 'custom', + title: this.translateService.instant('ACTIONS'), + valuePrepareFunction: (cell: RoleData, row: RoleData): RoleData => row, + renderComponent: RolesActionComponent + } + }; + } + + /** smart table Data Settings @public */ + public generateSettings(): void { + this.settings = { + edit: { + editButtonContent: '', + confirmSave: true + }, + delete: { + deleteButtonContent: '', + confirmDelete: true + }, + columns: this.columnLists, + actions: { + add: false, + edit: false, + delete: false, + topology: false, + position: 'right' + }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** smart table listing manipulation @public */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** convert UserRowSelect Function @private */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'roles' }); + this.dataService.changeMessage(event.data); + } + + /** Fetching the role data from API and Load it in the smarttable @public */ + public generateData(): void { + this.isLoadingResults = true; + this.roleData = []; + this.restService.getResource(environment.ROLES_URL).subscribe((roleList: RoleDetails[]) => { + roleList.forEach((role: RoleDetails) => { + const roleDataObj: RoleData = this.generateRoleData(role); + this.roleData.push(roleDataObj); + }); + this.dataSource.load(this.roleData).then((data: boolean) => { + this.isLoadingResults = false; + }).catch(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** Generate role data object and return for the datasource @public */ + public generateRoleData(roleData: RoleDetails): RoleData { + return { + name: roleData.name, + identifier: roleData._id, + modified: this.sharedService.convertEpochTime(Number(roleData._admin.modified)), + created: this.sharedService.convertEpochTime(Number(roleData._admin.created)), + permissions: roleData.permissions + }; + } + + /** Create role click handler @public */ + public createRole(): void { + this.router.navigate(['/roles/create']).catch(() => { + // Catch Navigation Error + }); + } + + /** Lifecyle hook which get trigger on component destruction @private */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } + +} diff --git a/src/app/sdn-controller/SDNControllerComponent.html b/src/app/sdn-controller/SDNControllerComponent.html new file mode 100644 index 0000000..3f96ff8 --- /dev/null +++ b/src/app/sdn-controller/SDNControllerComponent.html @@ -0,0 +1,18 @@ + + \ No newline at end of file diff --git a/src/app/sdn-controller/SDNControllerComponent.scss b/src/app/sdn-controller/SDNControllerComponent.scss new file mode 100644 index 0000000..5a72947 --- /dev/null +++ b/src/app/sdn-controller/SDNControllerComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ diff --git a/src/app/sdn-controller/SDNControllerComponent.ts b/src/app/sdn-controller/SDNControllerComponent.ts new file mode 100644 index 0000000..661882c --- /dev/null +++ b/src/app/sdn-controller/SDNControllerComponent.ts @@ -0,0 +1,55 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file SDN Controller Component. + */ +import { Component, Injector } from '@angular/core'; +import { Router, RouterEvent } from '@angular/router'; + +/** + * Creating component + * @Component takes SDNControllerComponent.html as template url + */ +@Component({ + templateUrl: './SDNControllerComponent.html', + styleUrls: ['./SDNControllerComponent.scss'] +}) +/** Exporting a class @exports SDNControllerComponent */ +export class SDNControllerComponent { + /** Invoke service injectors @public */ + public injector: Injector; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + // creates packages component + constructor(injector: Injector) { + this.injector = injector; + this.router = this.injector.get(Router); + this.router.events.subscribe((event: RouterEvent) => { + this.redirectToList(event.url); + }); + } + + /** Return to list NS Package List */ + public redirectToList(getURL: string): void { + if (getURL === '/sdn') { + this.router.navigate(['/sdn/details']).catch(); + } + } +} diff --git a/src/app/sdn-controller/SDNControllerModule.ts b/src/app/sdn-controller/SDNControllerModule.ts new file mode 100644 index 0000000..6da4a5c --- /dev/null +++ b/src/app/sdn-controller/SDNControllerModule.ts @@ -0,0 +1,82 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file SDN Controller module. + */ +import { CommonModule } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterModule, Routes } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { TranslateModule } from '@ngx-translate/core'; +import { DataService } from 'DataService'; +import { LoaderModule } from 'LoaderModule'; +import { NewSDNControllerComponent } from 'NewSDNControllerComponent'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { PagePerRowModule } from 'PagePerRowModule'; +import { PageReloadModule } from 'PageReloadModule'; +import { SDNControllerComponent } from 'SDNControllerComponent'; +import { SDNControllerDetailsComponent } from 'SDNControllerDetailsComponent'; +import { SDNControllerInfoComponent } from 'SDNControllerInfoComponent'; + +/** To halndle project information */ +const projectInfo: {} = localStorage.getItem('project') !== null ? { title: localStorage.getItem('project'), url: '/' } : {}; + +/** + * configures routers + */ +const routes: Routes = [ + { + path: '', + component: SDNControllerComponent, + children: [ + { + path: 'details', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'SDNCONTROLLER', url: null }] + }, + component: SDNControllerDetailsComponent + } + ] + } +]; + +/** + * Creating @NgModule component for Modules + */ +@NgModule({ + imports: [ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), FormsModule, CommonModule, + HttpClientModule, NgSelectModule, Ng2SmartTableModule, TranslateModule, RouterModule.forChild(routes), NgbModule, + PagePerRowModule, LoaderModule, PageReloadModule], + declarations: [SDNControllerComponent, SDNControllerDetailsComponent, SDNControllerInfoComponent, NewSDNControllerComponent], + providers: [DataService], + entryComponents: [SDNControllerInfoComponent, NewSDNControllerComponent], + schemas: [CUSTOM_ELEMENTS_SCHEMA] +}) +/** Exporting a class @exports SDNControllerModule */ +export class SDNControllerModule { + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + //Empty Class + } +} diff --git a/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.html b/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.html new file mode 100644 index 0000000..7b909b1 --- /dev/null +++ b/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.html @@ -0,0 +1,104 @@ + +
+ + + +
+ diff --git a/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.scss b/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.ts b/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.ts new file mode 100644 index 0000000..75fc854 --- /dev/null +++ b/src/app/sdn-controller/new-sdn-controller/NewSDNControllerComponent.ts @@ -0,0 +1,151 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file SDN Controller Component. + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, Injector, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA, SDN_TYPES, TYPESECTION } from 'CommonModel'; +import { environment } from 'environment'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes NewSDNControllerComponent.html as template url + */ +@Component({ + templateUrl: './NewSDNControllerComponent.html', + styleUrls: ['./NewSDNControllerComponent.scss'] +}) +/** Exporting a class @exports NewSDNControllerComponent */ +export class NewSDNControllerComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Set SDN Type select to empty @public */ + public sdnTypeMod: string = null; + + /** Setting SDN types in array @public */ + public sdnType: TYPESECTION[]; + + /** New SDN controller form controls using formgroup @public */ + public sdnControllerForm: FormGroup; + + /** Form submission Add */ + public submitted: boolean = false; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Check the loading results for loader status @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.formBuilder = this.injector.get(FormBuilder); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.activeModal = this.injector.get(NgbActiveModal); + this.sharedService = this.injector.get(SharedService); + + /** Initializing Form Action */ + this.sdnControllerForm = this.formBuilder.group({ + name: ['', Validators.required], + type: ['', Validators.required], + user: ['', Validators.required], + password: ['', Validators.required], + ip: ['', Validators.pattern(this.sharedService.REGX_IP_PATTERN)], + port: ['', Validators.pattern(this.sharedService.REGX_PORT_PATTERN)], + dpid: ['', Validators.pattern(this.sharedService.REGX_DPID_PATTERN)], + version: [''] + }); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.sdnControllerForm.controls; } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.sdnType = SDN_TYPES; + this.headers = new HttpHeaders({ + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + } + + /** On modal submit sdnControllerFormSubmit will called @public */ + public sdnControllerFormSubmit(): void { + this.submitted = true; + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + this.sharedService.cleanForm(this.sdnControllerForm); + if (!this.sdnControllerForm.invalid) { + this.isLoadingResults = true; + const apiURLHeader: APIURLHEADER = { + url: environment.SDNCONTROLLER_URL, + httpOptions: { headers: this.headers } + }; + if (this.sdnControllerForm.value.port) { + this.sdnControllerForm.value.port = +this.sdnControllerForm.value.port; + } + if (this.sdnControllerForm.value.version === '') { + this.sdnControllerForm.value.version = undefined; + } + this.restService.postResource(apiURLHeader, this.sdnControllerForm.value) + .subscribe((result: {}) => { + this.activeModal.close(modalData); + this.isLoadingResults = false; + this.notifierService.notify('success', this.translateService.instant('PAGE.SDNCONTROLLER.CREATEDSUCCESSFULLY')); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + this.isLoadingResults = false; + }); + } + } +} diff --git a/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.html b/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.html new file mode 100644 index 0000000..c316942 --- /dev/null +++ b/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.html @@ -0,0 +1,43 @@ + +
+
{{'PAGE.SDNCONTROLLER.REGISTEREDSDNCONTROLLER' | translate}}
+ + + +
+
+
+ +
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.scss b/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.scss new file mode 100644 index 0000000..8c2b739 --- /dev/null +++ b/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ diff --git a/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.ts b/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.ts new file mode 100644 index 0000000..125d0f4 --- /dev/null +++ b/src/app/sdn-controller/sdn-controller-details/SDNControllerDetailsComponent.ts @@ -0,0 +1,249 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file SDN Controller details Component. + */ +import { Component, Injector, OnDestroy, OnInit } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { CONFIGCONSTANT, ERRORDATA, MODALCLOSERESPONSEDATA, SDN_TYPES } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { NewSDNControllerComponent } from 'NewSDNControllerComponent'; +import { LocalDataSource } from 'ng2-smart-table'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SDNControllerActionComponent } from 'SDNControllerActionComponent'; +import { SDNControllerList, SDNControllerModel } from 'SDNControllerModel'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes SDNControllerDetailsComponent.html as template url + */ +@Component({ + templateUrl: './SDNControllerDetailsComponent.html', + styleUrls: ['./SDNControllerDetailsComponent.scss'] +}) +/** Exporting a class @exports SDNControllerDetailsComponent */ +export class SDNControllerDetailsComponent implements OnInit, OnDestroy { + /** Injector to invoke other services @public */ + public injector: Injector; + + /** Selected list array @public */ + public selectList: object[] = []; + + /** Instance component are stored in settings @public */ + public settings: {} = {}; + + /** Contains objects for menu settings @public */ + public columnList: {} = {}; + + /** Data of smarttable populate through LocalDataSource @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** operational State init data @public */ + public operationalStateFirstStep: string = CONFIGCONSTANT.sdnOperationalStateFirstStep; + + /** operational State running data @public */ + public operationalStateSecondStep: string = CONFIGCONSTANT.sdnOperationalStateStateSecondStep; + + /** operational State failed data @public */ + public operationalStateThirdStep: string = CONFIGCONSTANT.sdnOperationalStateThirdStep; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Formation of appropriate Data for LocalDatasource @private */ + private sdnData: {}[] = []; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + this.dataService = this.injector.get(DataService); + this.modalService = this.injector.get(NgbModal); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.generateTableColumn(); + this.generateTableSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** Generate smart table row title and filters @public */ + public generateTableSettings(): void { + this.settings = { + columns: this.columnList, + actions: { add: false, edit: false, delete: false, position: 'right' }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** Generate smart table row title and filters @public */ + public generateTableColumn(): void { + this.columnList = { + name: { title: this.translateService.instant('NAME'), width: '15%', sortDirection: 'asc' }, + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' }, + type: { + title: this.translateService.instant('TYPE'), width: '15%', + filter: { + type: 'list', + config: { + selectText: 'Select', + list: SDN_TYPES + } + } + }, + operationalState: { + title: this.translateService.instant('OPERATIONALSTATUS'), width: '15%', type: 'html', + filter: { + type: 'list', + config: { + selectText: 'Select', + list: [ + { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep }, + { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep }, + { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep } + ] + } + }, + valuePrepareFunction: (cell: SDNControllerList, row: SDNControllerList): string => { + if (row.operationalState === this.operationalStateFirstStep) { + return ` + + `; + } else if (row.operationalState === this.operationalStateSecondStep) { + return ` + + `; + } else if (row.operationalState === this.operationalStateThirdStep) { + return ` + + `; + } else { + return `${row.operationalState}`; + } + } + }, + ip: { title: this.translateService.instant('IP'), width: '15%' }, + port: { title: this.translateService.instant('PORT'), width: '15%' }, + Actions: { + name: 'Action', width: '5%', filter: false, sort: false, type: 'custom', + title: this.translateService.instant('ACTIONS'), + valuePrepareFunction: (cell: SDNControllerList, row: SDNControllerList): SDNControllerList => row, + renderComponent: SDNControllerActionComponent + } + }; + } + + /** smart table listing manipulation @public */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** smart table listing manipulation @public */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'sdn-controller' }); + this.dataService.changeMessage(event.data); + } + + /** Generate generateSDNData object from loop and return for the datasource @public */ + public generateSDNList(sdn: SDNControllerModel): SDNControllerList { + return { + name: sdn.name, + identifier: sdn._id, + type: sdn.type, + operationalState: sdn._admin.operationalState, + ip: sdn.ip, + port: sdn.port + }; + } + + /** Compose new SDN Controller @public */ + public composeSDN(): void { + const modalRef: NgbModalRef = this.modalService.open(NewSDNControllerComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } + + /** Fetching the data from server to Load in the smarttable @protected */ + protected generateData(): void { + this.isLoadingResults = true; + this.sdnData = []; + this.restService.getResource(environment.SDNCONTROLLER_URL).subscribe((sdnDetails: {}[]) => { + sdnDetails.forEach((res: SDNControllerModel) => { + const sdnDataObj: SDNControllerList = this.generateSDNList(res); + this.sdnData.push(sdnDataObj); + }); + if (this.sdnData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.sdnData).then((data: {}) => { + this.isLoadingResults = false; + }).catch(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + +} diff --git a/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.html b/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.html new file mode 100644 index 0000000..842d101 --- /dev/null +++ b/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.html @@ -0,0 +1,90 @@ + + + + + \ No newline at end of file diff --git a/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.scss b/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.scss new file mode 100644 index 0000000..c68960c --- /dev/null +++ b/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.ts b/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.ts new file mode 100644 index 0000000..8c23d1d --- /dev/null +++ b/src/app/sdn-controller/sdn-controller-info/SDNControllerInfoComponent.ts @@ -0,0 +1,96 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Info SDN Controller Info Component + */ +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { CONFIGCONSTANT, ERRORDATA, URLPARAMS } from 'CommonModel'; +import { environment } from 'environment'; +import { RestService } from 'RestService'; +import { SDNControllerModel } from 'SDNControllerModel'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes SDNControllerInfoComponent.html as template url + */ +@Component({ + templateUrl: './SDNControllerInfoComponent.html', + styleUrls: ['./SDNControllerInfoComponent.scss'] +}) +/** Exporting a class @exports SDNControllerInfoComponent */ +export class SDNControllerInfoComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Input contains component objects @public */ + @Input() public params: URLPARAMS; + + /** Contains sdn details @public */ + public sdnDetails: SDNControllerModel; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Check the loading results for loader status @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** operational State init data @public */ + public operationalStateFirstStep: string = CONFIGCONSTANT.sdnOperationalStateFirstStep; + + /** operational State running data @public */ + public operationalStateSecondStep: string = CONFIGCONSTANT.sdnOperationalStateStateSecondStep; + + /** operational State failed data @public */ + public operationalStateThirdStep: string = CONFIGCONSTANT.sdnOperationalStateThirdStep; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + this.sharedService = this.injector.get(SharedService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.generateData(); + } + + /** Generate Data function @public */ + public generateData(): void { + this.restService.getResource(environment.SDNCONTROLLER_URL + '/' + this.params.id).subscribe((sdnDetails: SDNControllerModel) => { + this.sdnDetails = sdnDetails; + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } +} diff --git a/src/app/user-settings/UserSettingsComponent.html b/src/app/user-settings/UserSettingsComponent.html new file mode 100644 index 0000000..2f878a3 --- /dev/null +++ b/src/app/user-settings/UserSettingsComponent.html @@ -0,0 +1,44 @@ + +
+ + + +
\ No newline at end of file diff --git a/src/app/user-settings/UserSettingsComponent.scss b/src/app/user-settings/UserSettingsComponent.scss new file mode 100644 index 0000000..bdfb8f2 --- /dev/null +++ b/src/app/user-settings/UserSettingsComponent.scss @@ -0,0 +1,23 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +@import '../../assets/scss/mixins/mixin'; +.user-settings{ + form label{ + @include margin-value-percentage(0.5rem, auto, auto, auto); + } +} \ No newline at end of file diff --git a/src/app/user-settings/UserSettingsComponent.ts b/src/app/user-settings/UserSettingsComponent.ts new file mode 100644 index 0000000..19b525f --- /dev/null +++ b/src/app/user-settings/UserSettingsComponent.ts @@ -0,0 +1,111 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file User Settings Modal Component. + */ + +import { Component, Injector, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes UserSettingsComponent.html as template url + */ +@Component({ + templateUrl: './UserSettingsComponent.html', + styleUrls: ['./UserSettingsComponent.scss'] +}) +/** Exporting a class @exports UserSettingsComponent */ +export class UserSettingsComponent implements OnInit { + /** Invoke service injectors @public */ + public injector: Injector; + + /** Supported language list for the dropdown */ + public languageList: {}[]; + + /** FormGroup instance added to the form @ html @public */ + public usersettingsForm: FormGroup; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Form submission Add */ + public submitted: boolean = false; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Instance for translate service @private */ + private translateService: TranslateService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.formBuilder = this.injector.get(FormBuilder); + this.activeModal = this.injector.get(NgbActiveModal); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.initializeSettings(); + } + + /** Initialize user's settings */ + public initializeSettings(): void { + this.languageList = this.sharedService.languageCodeList(); + /** Initializing Form Action */ + this.usersettingsForm = this.formBuilder.group({ + selectedLanguage: [null, [Validators.required]] + }); + const setLanguage: string = localStorage.getItem('languageCode'); + if (setLanguage !== null && this.validateLanguageList(setLanguage)) { + // tslint:disable-next-line:no-backbone-get-set-outside-model + this.usersettingsForm.get('selectedLanguage').setValue(setLanguage); + } else { + // tslint:disable-next-line:no-backbone-get-set-outside-model + this.usersettingsForm.get('selectedLanguage').setValue('en'); + } + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.usersettingsForm.controls; } + + /** On modal submit UserSettingsSubmit will called @public */ + public usersettingsSubmit(): void { + this.submitted = true; + if (!this.usersettingsForm.invalid) { + const selectedLanguage: string = this.usersettingsForm.value.selectedLanguage; + localStorage.setItem('languageCode', this.usersettingsForm.value.selectedLanguage); + this.translateService.use(selectedLanguage); + location.reload(); + } + } + /** Validate language code in the language list @private */ + private validateLanguageList(setLanguage: string): boolean { + return this.languageList.some((item: { code: string }) => item.code === setLanguage); + } +} diff --git a/src/app/users/UsersComponent.html b/src/app/users/UsersComponent.html new file mode 100644 index 0000000..3f96ff8 --- /dev/null +++ b/src/app/users/UsersComponent.html @@ -0,0 +1,18 @@ + + \ No newline at end of file diff --git a/src/app/users/UsersComponent.scss b/src/app/users/UsersComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/users/UsersComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/users/UsersComponent.ts b/src/app/users/UsersComponent.ts new file mode 100644 index 0000000..178b979 --- /dev/null +++ b/src/app/users/UsersComponent.ts @@ -0,0 +1,55 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file users details Component. + */ +import { Component, Injector } from '@angular/core'; +import { Router, RouterEvent } from '@angular/router'; + +/** + * Creating component + * @Component takes UsersComponent.html as template url + */ +@Component({ + templateUrl: './UsersComponent.html', + styleUrls: ['./UsersComponent.scss'] +}) +/** Exporting a class @exports UsersComponent */ +export class UsersComponent { + /** Invoke service injectors @public */ + public injector: Injector; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + // creates packages component + constructor(injector: Injector) { + this.injector = injector; + this.router = this.injector.get(Router); + this.router.events.subscribe((event: RouterEvent) => { + this.redirectToList(event.url); + }); + } + + /** Return to list NS Package List */ + public redirectToList(getURL: string): void { + if (getURL === '/users') { + this.router.navigate(['/users/details']).catch(); + } + } +} diff --git a/src/app/users/UsersModule.ts b/src/app/users/UsersModule.ts new file mode 100644 index 0000000..2014c48 --- /dev/null +++ b/src/app/users/UsersModule.ts @@ -0,0 +1,76 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Users module. + */ +import { CommonModule } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; +import { NgModule } from '@angular/core'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { FormsModule } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterModule, Routes } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddEditUserComponent } from 'AddEditUserComponent'; +import { DataService } from 'DataService'; +import { LoaderModule } from 'LoaderModule'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { PagePerRowModule } from 'PagePerRowModule'; +import { PageReloadModule } from 'PageReloadModule'; +import { ProjectRoleComponent } from 'ProjectRoleComponent'; +import { UserDetailsComponent } from 'UserDetailsComponent'; +import { UsersComponent } from 'UsersComponent'; + +/** const values for dashboard Routes */ +const routes: Routes = [ + { + path: '', + component: UsersComponent, + children: [ + { + path: 'details', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.USERS', url: null }] + }, + component: UserDetailsComponent + } + ] + } +]; + +/** + * Creating @NgModule component for Modules + */ +@NgModule({ + imports: [ReactiveFormsModule, FormsModule, CommonModule, HttpClientModule, Ng2SmartTableModule, TranslateModule, + FlexLayoutModule, NgSelectModule, NgbModule, RouterModule.forChild(routes), PagePerRowModule, LoaderModule, PageReloadModule], + declarations: [UsersComponent, UserDetailsComponent, AddEditUserComponent, ProjectRoleComponent], + providers: [DataService], + entryComponents: [AddEditUserComponent, ProjectRoleComponent] +}) +/** Exporting a class @exports UsersModule */ +export class UsersModule { + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + // Empty Block + } +} diff --git a/src/app/users/add-user/AddEditUserComponent.html b/src/app/users/add-user/AddEditUserComponent.html new file mode 100644 index 0000000..b4d9d28 --- /dev/null +++ b/src/app/users/add-user/AddEditUserComponent.html @@ -0,0 +1,89 @@ + +
+ + + +
+ \ No newline at end of file diff --git a/src/app/users/add-user/AddEditUserComponent.scss b/src/app/users/add-user/AddEditUserComponent.scss new file mode 100644 index 0000000..05f2819 --- /dev/null +++ b/src/app/users/add-user/AddEditUserComponent.scss @@ -0,0 +1,25 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +@import '../../../assets/scss/mixins/mixin'; +@import '../../../assets/scss/variable'; +.input-validation-msg{ + color:$red; + text-align:left; + @include padding-value(0, 0, 0, 10); + @include font(null, 11px, null); +} \ No newline at end of file diff --git a/src/app/users/add-user/AddEditUserComponent.ts b/src/app/users/add-user/AddEditUserComponent.ts new file mode 100644 index 0000000..61540e8 --- /dev/null +++ b/src/app/users/add-user/AddEditUserComponent.ts @@ -0,0 +1,255 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Add Edit Component. + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { AuthenticationService } from 'AuthenticationService'; +import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { environment } from 'environment'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; + +/** + * Creating component + * @Component takes AddEditUserComponent.html as template url + */ +@Component({ + templateUrl: './AddEditUserComponent.html', + styleUrls: ['./AddEditUserComponent.scss'] +}) +/** Exporting a class @exports AddEditUserComponent */ +export class AddEditUserComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** FormGroup user Edit Account added to the form @ html @public */ + public userForm: FormGroup; + + /** Form submission Add */ + public submitted: boolean = false; + + /** Input contains Modal dialog component Instance @public */ + @Input() public userTitle: string; + + /** Input contains Modal dialog component Instance @public */ + @Input() public userType: string; + + /** Input contains Modal dialog component Instance @public */ + @Input() public userID: string; + + /** Input contains Modal dialog component Instance @public */ + @Input() public userName: string; + + /** Check the loading results for loader status @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Holds list of domains @public */ + public domains: {}[] = []; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** ModalData instance of modal @private */ + private modalData: MODALCLOSERESPONSEDATA; + + /** Utilizes auth service for any auth operations @private */ + private authService: AuthenticationService; + + constructor(injector: Injector) { + this.injector = injector; + this.formBuilder = this.injector.get(FormBuilder); + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + this.authService = this.injector.get(AuthenticationService); + + /** Initializing Form Action */ + this.userForm = this.formBuilder.group({ + userName: ['', Validators.required], + password: [null, [Validators.required, Validators.pattern(this.sharedService.REGX_PASSWORD_PATTERN)]], + password2: [null, Validators.required], + domain_name: [null] + }); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.userForm.controls; } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.headers = new HttpHeaders({ + 'Content-Type': 'application/json', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + if (this.userType === 'add') { + this.getDomainName(); + } else if (this.userType === 'editUserName') { + this.userForm.patchValue({ userName: this.userName }); + } + } + + /** On modal submit users acction will called @public */ + public userAction(userType: string): void { + if (userType === 'editPassword') { + this.getFormControl('userName').setValidators([]); + this.getFormControl('userName').updateValueAndValidity(); + } else if (userType === 'editUserName') { + this.getFormControl('password').setValidators([]); + this.getFormControl('password').updateValueAndValidity(); + this.getFormControl('password2').setValidators([]); + this.getFormControl('password2').updateValueAndValidity(); + } + this.submitted = true; + this.modalData = { + message: 'Done' + }; + this.sharedService.cleanForm(this.userForm); + if (!this.userForm.invalid) { + if (this.userForm.value.password !== this.userForm.value.password2) { + this.notifierService.notify('error', this.translateService.instant('PAGE.USERS.PASSWORDCONFLICT')); + return; + } + if (userType === 'add') { + this.addUser(); + } else if (userType === 'editUserName' || userType === 'editPassword') { + this.editUser(); + } + } + } + + /** Add user @public */ + public addUser(): void { + this.isLoadingResults = true; + const payLoad: {} = JSON.stringify({ + username: (this.userForm.value.userName).toLowerCase(), + password: (this.userForm.value.password), + domain_name: !isNullOrUndefined(this.userForm.value.domain_name) ? this.userForm.value.domain_name : undefined + }); + const apiURLHeader: APIURLHEADER = { + url: environment.USERS_URL, + httpOptions: { headers: this.headers } + }; + this.restService.postResource(apiURLHeader, payLoad).subscribe((result: {}) => { + this.activeModal.close(this.modalData); + this.isLoadingResults = false; + this.notifierService.notify('success', this.translateService.instant('PAGE.USERS.CREATEDSUCCESSFULLY')); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + this.isLoadingResults = false; + }); + } + + /** Edit user @public */ + public editUser(): void { + this.isLoadingResults = true; + const payLoad: { username?: string, password?: string } = {}; + if (this.userType === 'editPassword') { + payLoad.password = (this.userForm.value.password); + } else { + payLoad.username = this.userForm.value.userName.toLowerCase(); + } + const apiURLHeader: APIURLHEADER = { + url: environment.USERS_URL + '/' + this.userID, + httpOptions: { headers: this.headers } + }; + this.restService.patchResource(apiURLHeader, payLoad).subscribe((result: {}) => { + this.checkUsername(payLoad); + this.activeModal.close(this.modalData); + this.isLoadingResults = false; + this.notifierService.notify('success', this.translateService.instant('PAGE.USERS.EDITEDSUCCESSFULLY')); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'put'); + this.isLoadingResults = false; + }); + } + /** Get domain name @private */ + private getDomainName(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.DOMAIN_URL).subscribe((domains: { project_domain_name: string, user_domain_name: string }) => { + let domainNames: string[] = []; + if (!isNullOrUndefined(domains.project_domain_name)) { + domainNames = domainNames.concat(domains.project_domain_name.split(',')); + } + if (!isNullOrUndefined(domains.user_domain_name)) { + domainNames = domainNames.concat(domains.user_domain_name.split(',')); + } + domainNames = Array.from(new Set(domainNames)); + this.checkDomainNames(domainNames); + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** Check the domain names and create modal for domain select @private */ + private checkDomainNames(domainNames: string[]): void { + if (domainNames.length > 0) { + domainNames.forEach((domainName: string) => { + if (!domainName.endsWith(':ro')) { + this.domains.push({ id: domainName, text: domainName }); + } + }); + } + } + + /** Used to get the AbstractControl of controlName passed @private */ + private getFormControl(controlName: string): AbstractControl { + return this.userForm.controls[controlName]; + } + + /** Method to check loggedin username and update @private */ + private checkUsername(payLoad: { username?: string }): void { + const logUsername: string = localStorage.getItem('username'); + if (this.userType === 'editUserName' && logUsername === this.userName) { + this.authService.userName.next(payLoad.username); + localStorage.setItem('username', payLoad.username); + } + } +} diff --git a/src/app/users/project-role/ProjectRoleComponent.html b/src/app/users/project-role/ProjectRoleComponent.html new file mode 100644 index 0000000..c093f37 --- /dev/null +++ b/src/app/users/project-role/ProjectRoleComponent.html @@ -0,0 +1,60 @@ + + +
+ + +
+ \ No newline at end of file diff --git a/src/app/users/project-role/ProjectRoleComponent.scss b/src/app/users/project-role/ProjectRoleComponent.scss new file mode 100644 index 0000000..031e56e --- /dev/null +++ b/src/app/users/project-role/ProjectRoleComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/users/project-role/ProjectRoleComponent.ts b/src/app/users/project-role/ProjectRoleComponent.ts new file mode 100644 index 0000000..f5bb772 --- /dev/null +++ b/src/app/users/project-role/ProjectRoleComponent.ts @@ -0,0 +1,236 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Project Role Component. + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { environment } from 'environment'; +import { ProjectData } from 'ProjectModel'; +import { ProjectService } from 'ProjectService'; +import { RestService } from 'RestService'; +import { RoleData } from 'RolesModel'; +import { ProjectRoleMappings, UserDetail, UserRoleMap } from 'UserModel'; + +/** + * Creating component + * @Component takes ProjectRole.html as template url + */ +@Component({ + templateUrl: './ProjectRoleComponent.html', + styleUrls: ['./ProjectRoleComponent.scss'] +}) +/** Exporting a class @exports ProjectRoleComponent */ +export class ProjectRoleComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** FormGroup user Edit Account added to the form @ html @public */ + public projectRoleForm: FormGroup; + + /** Form submission Add */ + public submitted: boolean = false; + + /** Input contains Modal dialog component Instance @private */ + @Input() public userTitle: string; + + /** Input contains Modal dialog component Instance @private */ + @Input() public userID: string; + + /** Contains user details information @public */ + public userDetails: UserDetail; + + /** Project Role Mapping @public */ + public projectRoleMap: UserRoleMap = {}; + + /** Check the loading results @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Contains project information @public */ + public projects: ProjectData[] = []; + + /** Contains roles information @public */ + public roles: RoleData[] = []; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Project Role Form array @private */ + private projectRoleFormArray: FormArray; + + /** Holds all project details @private */ + private projectService: ProjectService; + + constructor(injector: Injector) { + this.injector = injector; + this.formBuilder = this.injector.get(FormBuilder); + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.projectService = this.injector.get(ProjectService); + this.initializeForm(); + } + + /** Generate primitive params @public */ + get projectRoleParamsBuilder(): FormGroup { + return this.formBuilder.group({ + project_name: [null, [Validators.required]], + role_name: [null, [Validators.required]] + }); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.headers = new HttpHeaders({ + 'Content-Type': 'application/json', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + this.getProjects(); + this.generateData(); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.projectRoleForm.controls; } + + /** Initializing Form Action @public */ + public initializeForm(): void { + this.projectRoleForm = this.formBuilder.group({ + project_role_mappings: this.formBuilder.array([]) + }); + } + + /** Handle FormArray Controls @public */ + public getControls(): AbstractControl[] { + // tslint:disable-next-line:no-backbone-get-set-outside-model + return (this.projectRoleForm.get('project_role_mappings') as FormArray).controls; + } + + /** Fetching the data from server to Load in the smarttable @public */ + public generateData(): void { + if (this.userID !== '') { + this.isLoadingResults = true; + this.restService.getResource(environment.USERS_URL + '/' + this.userID).subscribe((userDetails: UserDetail) => { + this.userDetails = userDetails; + this.loadMapping(); + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }); + } + } + /** Fetching the projects information @public */ + public getProjects(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.PROJECTS_URL).subscribe((projectsData: ProjectData[]) => { + this.projects = projectsData; + this.getRoles(); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }); + } + + /** Fetching the Roles information @public */ + public getRoles(): void { + this.restService.getResource(environment.ROLES_URL).subscribe((rolesData: RoleData[]) => { + this.roles = rolesData; + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }); + } + + /** Set all roles and project values to the form @public */ + public loadMapping(): void { + this.userDetails.project_role_mappings.forEach((data: ProjectRoleMappings) => { + // tslint:disable-next-line:no-backbone-get-set-outside-model + this.projectRoleFormArray = this.projectRoleForm.get('project_role_mappings') as FormArray; + this.projectRoleFormArray.push(this.projectRoleParamsBuilder); + }); + this.projectRoleForm.patchValue(this.userDetails); + } + + /** Remove project and roles from the list @public */ + public removeMapping(index: number): void { + this.projectRoleFormArray.removeAt(index); + } + + /** Submit project and roles @public */ + public addProjectRole(): void { + this.submitted = true; + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + if (this.projectRoleForm.invalid) { return; } + const apiURLHeader: APIURLHEADER = { + url: environment.USERS_URL + '/' + this.userID + }; + this.projectRoleMap.project_role_mappings = []; + this.projectRoleForm.value.project_role_mappings.forEach((res: ProjectRoleMappings) => { + this.projectRoleMap.project_role_mappings.push({ project: res.project_name, role: res.role_name }); + }); + if (this.projectRoleMap.project_role_mappings.length !== 0) { + this.isLoadingResults = true; + this.restService.patchResource(apiURLHeader, this.projectRoleMap).subscribe((result: {}) => { + this.isLoadingResults = false; + this.activeModal.close(modalData); + this.projectService.setHeaderProjects(); + this.notifierService.notify('success', this.translateService.instant('PAGE.USERS.EDITEDSUCCESSFULLY')); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'patch'); + }); + } else { + this.notifierService.notify('error', this.translateService.instant('PAGE.USERS.EDITPROJECTROLEERROR')); + } + } + + /** Add extra mapping and set empty project and roles @public */ + public addMapping(): void { + // tslint:disable-next-line:no-backbone-get-set-outside-model + this.projectRoleFormArray = this.projectRoleForm.get('project_role_mappings') as FormArray; + this.projectRoleFormArray.push(this.projectRoleParamsBuilder); + } +} diff --git a/src/app/users/user-details/UserDetailsComponent.html b/src/app/users/user-details/UserDetailsComponent.html new file mode 100644 index 0000000..9d11186 --- /dev/null +++ b/src/app/users/user-details/UserDetailsComponent.html @@ -0,0 +1,36 @@ + +
+
{{'PAGE.DASHBOARD.USERS' | translate}}
+ + + +
+
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/users/user-details/UserDetailsComponent.scss b/src/app/users/user-details/UserDetailsComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/users/user-details/UserDetailsComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/users/user-details/UserDetailsComponent.ts b/src/app/users/user-details/UserDetailsComponent.ts new file mode 100644 index 0000000..a4bedd5 --- /dev/null +++ b/src/app/users/user-details/UserDetailsComponent.ts @@ -0,0 +1,214 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file users details Component. + */ +import { Component, Injector, OnDestroy, OnInit } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { AddEditUserComponent } from 'AddEditUserComponent'; +import { ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { LocalDataSource } from 'ng2-smart-table'; +import { ProjectService } from 'ProjectService'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; +import { UserData, UserDetail } from 'UserModel'; +import { UsersActionComponent } from 'UsersActionComponent'; + +/** + * Creating component + * @Component takes UserDetailsComponent.html as template url + */ +@Component({ + templateUrl: './UserDetailsComponent.html', + styleUrls: ['./UserDetailsComponent.scss'] +}) +/** Exporting a class @exports UserDetailsComponent */ +export class UserDetailsComponent implements OnInit, OnDestroy { + /** Data of smarttable populate through LocalDataSource @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** handle translate @public */ + public translateService: TranslateService; + + /** To inject services @public */ + public injector: Injector; + + /** Settings for smarttable to populate the table with columns @public */ + public settings: object = {}; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Formation of appropriate Data for LocalDatasource @private */ + private userData: {}[] = []; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Holds all project details */ + private projectService: ProjectService; + + /** holds the project information @private */ + private projectList: {}[] = []; + + /** Columns list of the smart table @public */ + private columnLists: object = {}; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.modalService = this.injector.get(NgbModal); + this.projectService = this.injector.get(ProjectService); + this.translateService = this.injector.get(TranslateService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.projectService.getAllProjects().subscribe((projects: {}[]) => { + this.projectList = projects; + }); + this.generateColumns(); + this.generateSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** smart table Header Colums @public */ + public generateColumns(): void { + this.columnLists = { + username: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' }, + projects: { title: this.translateService.instant('PAGE.DASHBOARD.PROJECTS'), width: '25%' }, + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' }, + modified: { title: this.translateService.instant('MODIFIED'), width: '15%' }, + created: { title: this.translateService.instant('CREATED'), width: '15%' }, + Actions: { + name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom', + valuePrepareFunction: (cell: UserData, row: UserData): UserData => row, + renderComponent: UsersActionComponent + } + }; + } + + /** smart table Data Settings @public */ + public generateSettings(): void { + this.settings = { + edit: { editButtonContent: '', confirmSave: true }, + delete: { deleteButtonContent: '', confirmDelete: true }, + columns: this.columnLists, + actions: { add: false, edit: false, delete: false, position: 'right' }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** on Navigate to Composer Page @public */ + public composeUser(): void { + const modalRef: NgbModalRef = this.modalService.open(AddEditUserComponent, { backdrop: 'static' }); + modalRef.componentInstance.userTitle = this.translateService.instant('PAGE.USERS.NEWUSER'); + modalRef.componentInstance.userType = 'add'; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** smart table listing manipulation @private */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** OnUserRowSelect function @private */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'users' }); + this.dataService.changeMessage(event.data); + } + + /** Set up user details @public */ + public setUserDetails(userData: UserDetail): void { + const userDataObj: UserData = { + username: userData.username, + modified: this.sharedService.convertEpochTime(userData._admin.modified), + created: this.sharedService.convertEpochTime(userData._admin.created), + projects: userData.projectListName, + identifier: userData._id + }; + this.userData.push(userDataObj); + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } + + /** Fetching the data from server to Load in the smarttable @protected */ + protected generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.USERS_URL).subscribe((usersData: UserDetail[]) => { + this.userData = []; + usersData.forEach((userData: UserDetail) => { + if (userData.projects.length > 0) { + userData.projectListName = userData.projects.join(', '); + } else { + userData.projectListName = ''; + } + this.setUserDetails(userData); + }); + if (this.userData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.userData).then((data: {}) => { + this.isLoadingResults = false; + }).catch(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } +} diff --git a/src/app/utilities/clone-package/ClonePackageComponent.html b/src/app/utilities/clone-package/ClonePackageComponent.html new file mode 100644 index 0000000..0a75c3a --- /dev/null +++ b/src/app/utilities/clone-package/ClonePackageComponent.html @@ -0,0 +1,33 @@ + + + + + \ No newline at end of file diff --git a/src/app/utilities/clone-package/ClonePackageComponent.scss b/src/app/utilities/clone-package/ClonePackageComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/clone-package/ClonePackageComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/clone-package/ClonePackageComponent.ts b/src/app/utilities/clone-package/ClonePackageComponent.ts new file mode 100644 index 0000000..94e0920 --- /dev/null +++ b/src/app/utilities/clone-package/ClonePackageComponent.ts @@ -0,0 +1,180 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file Clone Package Model + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, GETAPIURLHEADER, MODALCLOSERESPONSEDATA, URLPARAMS } from 'CommonModel'; +import { environment } from 'environment'; +import * as jsyaml from 'js-yaml'; +import { NSDDetails } from 'NSDModel'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes ClonePackageComponent.html as template url + */ + +@Component({ + selector: 'app-clone-package', + templateUrl: './ClonePackageComponent.html', + styleUrls: ['./ClonePackageComponent.scss'] +}) +/** Exporting a class @exports ClonePackageComponent */ +export class ClonePackageComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Input contains component objects @public */ + @Input() public params: URLPARAMS; + + /** To handle loader status for API call @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Contains cloned package name instance @private */ + private packageName: string = ''; + + /** Contains API end point for package creation @private */ + private endPoint: string; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.sharedService = this.injector.get(SharedService); + this.activeModal = this.injector.get(NgbActiveModal); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + } + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + // Empty Block + } + /** + * Get package information based on type + */ + public clonePackageInfo(): void { + let apiUrl: string = ''; + const httpOptions: GETAPIURLHEADER = this.getHttpoptions(); + apiUrl = this.params.page === 'nsd' ? apiUrl = environment.NSDESCRIPTORS_URL + '/' + this.params.id + '/nsd' : + apiUrl = environment.VNFPACKAGES_URL + '/' + this.params.id + '/vnfd'; + this.isLoadingResults = true; + this.restService.getResource(apiUrl, httpOptions) + .subscribe((nsData: NSDDetails[]) => { + this.modifyContent(nsData); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + error.error = typeof error.error === 'string' ? jsyaml.load(error.error) : error.error; + this.restService.handleError(error, 'get'); + }); + } + /** + * Get HTTP header options + */ + private getHttpoptions(): GETAPIURLHEADER { + const apiHeaders: HttpHeaders = new HttpHeaders({ + 'Content-Type': 'application/json', + Accept: 'text/plain', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + return { + headers: apiHeaders, + responseType: 'text' + }; + } + /** + * Get and modify package information based on type + */ + private modifyContent(packageInfo: NSDDetails[]): void { + const packageContent: string = jsyaml.load(packageInfo.toString()); + if (this.params.page === 'nsd') { + this.packageName = 'clone_' + packageContent['nsd:nsd-catalog'].nsd[0].name; + this.endPoint = environment.NSDESCRIPTORSCONTENT_URL; + packageContent['nsd:nsd-catalog'].nsd.forEach((nsd: NSDDetails) => { + nsd.id = 'clone_' + nsd.id; + nsd.name = 'clone_' + nsd.name; + nsd['short-name'] = 'clone_' + nsd['short-name']; + }); + } else { + this.packageName = 'clone_' + packageContent['vnfd:vnfd-catalog'].vnfd[0].name; + this.endPoint = environment.VNFPACKAGESCONTENT_URL; + packageContent['vnfd:vnfd-catalog'].vnfd.forEach((vnfd: NSDDetails) => { + vnfd.id = 'clone_' + vnfd.id; + vnfd.name = 'clone_' + vnfd.name; + vnfd['short-name'] = 'clone_' + vnfd['short-name']; + }); + } + this.clonePackage(packageContent); + } + /** + * Create clone package and upload as TAR.GZ file + */ + private clonePackage(packageContent: string): void { + const descriptorInfo: string = jsyaml.dump(packageContent); + const apiHeader: HttpHeaders = new HttpHeaders({ + 'Content-Type': 'application/gzip', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + this.sharedService.targzFile({ packageType: this.params.page, id: this.params.id, descriptor: descriptorInfo }) + .then((content: ArrayBuffer): void => { + const apiURLHeader: APIURLHEADER = { + url: this.endPoint, + httpOptions: { headers: apiHeader } + }; + this.restService.postResource(apiURLHeader, content).subscribe((result: { id: string }) => { + this.activeModal.close(modalData); + this.isLoadingResults = false; + this.notifierService.notify('success', this.translateService.instant('CLONESUCCESSFULLY')); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'post'); + }); + }).catch((): void => { + this.isLoadingResults = false; + this.notifierService.notify('error', this.translateService.instant('ERROR')); + }); + } +} diff --git a/src/app/utilities/compose-packages/ComposePackages.html b/src/app/utilities/compose-packages/ComposePackages.html new file mode 100644 index 0000000..0d66e0f --- /dev/null +++ b/src/app/utilities/compose-packages/ComposePackages.html @@ -0,0 +1,40 @@ + +
+ + + +
+ \ No newline at end of file diff --git a/src/app/utilities/compose-packages/ComposePackages.scss b/src/app/utilities/compose-packages/ComposePackages.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/compose-packages/ComposePackages.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/compose-packages/ComposePackages.ts b/src/app/utilities/compose-packages/ComposePackages.ts new file mode 100644 index 0000000..9567b43 --- /dev/null +++ b/src/app/utilities/compose-packages/ComposePackages.ts @@ -0,0 +1,237 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ +/** + * @file Info Compose Package Model + */ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, URLPARAMS } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import * as jsyaml from 'js-yaml'; +import * as pako from 'pako'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; + +/** This is added globally by the tar.js library */ +// tslint:disable-next-line: no-any +declare const Tar: any; + +/** + * Creating component + * @Component takes ComposePackages.html as template url + */ +@Component({ + templateUrl: './ComposePackages.html', + styleUrls: ['./ComposePackages.scss'] +}) +/** Exporting a class @exports ComposePackages */ +export class ComposePackages implements OnInit { + /** Invoke service injectors @public */ + public injector: Injector; + + /** dataService to pass the data from one component to another @public */ + public dataService: DataService; + + /** Varaibles to hold http client @public */ + public httpClient: HttpClient; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** FormGroup instance added to the form @ html @public */ + public packagesForm: FormGroup; + + /** Form submission Add */ + public submitted: boolean = false; + + /** To handle loader status for API call @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Create URL holds the end point of any packages @private */ + private createURL: string; + + /** Input contains component objects @private */ + @Input() private params: URLPARAMS; + + /** Holds the end point @private */ + private endPoint: string; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + constructor(injector: Injector) { + this.injector = injector; + this.dataService = this.injector.get(DataService); + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + this.notifierService = this.injector.get(NotifierService); + this.formBuilder = this.injector.get(FormBuilder); + this.router = this.injector.get(Router); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.headers = new HttpHeaders({ + 'Content-Type': 'application/gzip', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + this.initializeForm(); + } + + /** initialize Forms @public */ + public initializeForm(): void { + this.packagesForm = this.formBuilder.group({ + name: ['', [Validators.required]] + }); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.packagesForm.controls; } + + /** Create packages @public */ + public createPackages(): void { + this.submitted = true; + this.sharedService.cleanForm(this.packagesForm); + if (!this.packagesForm.invalid) { + this.isLoadingResults = true; + if (this.params.page === 'ns-package') { + this.endPoint = environment.NSDESCRIPTORSCONTENT_URL; + } else if (this.params.page === 'vnf-package') { + this.endPoint = environment.VNFPACKAGESCONTENT_URL; + } + const descriptor: string = this.packageYaml(this.params.page); + try { + // tslint:disable-next-line: no-any + const tar: any = new Tar(); + const out: Uint8Array = tar.append(this.packagesForm.value.name + '/' + this.packagesForm.value.name + '.yaml', + descriptor, { type: '0' }); + const gzipContent: Uint8Array = pako.gzip(out); + this.createPackageApi(gzipContent.buffer); + } catch (e) { + this.isLoadingResults = false; + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + } + } + /** Create packages @public */ + private createPackageApi(packageContent: ArrayBuffer | SharedArrayBuffer): void { + const apiURLHeader: APIURLHEADER = { + url: this.endPoint, + httpOptions: { headers: this.headers } + }; + this.restService.postResource(apiURLHeader, packageContent).subscribe((result: { id: string }) => { + this.isLoadingResults = false; + this.activeModal.close(); + this.composeNSPackages(result.id); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'post'); + }); + } + /** Compose NS Packages @private */ + private composeNSPackages(id: string): void { + let packageUrl: string; + if (this.params.page === 'ns-package') { + packageUrl = '/packages/ns/compose/'; + this.notifierService.notify('success', this.packagesForm.value.name + ' ' + + this.translateService.instant('PAGE.NSPACKAGE.CREATEDSUCCESSFULLY')); + } else if (this.params.page === 'vnf-package') { + packageUrl = '/packages/vnf/compose/'; + this.notifierService.notify('success', this.packagesForm.value.name + ' ' + + this.translateService.instant('PAGE.VNFPACKAGE.CREATEDSUCCESSFULLY')); + } + this.router.navigate([packageUrl, id]).catch(() => { + // Catch Navigation Error + }); + } + /** Deafult template for NS and VNF Packages @private */ + private packageYaml(descriptorType: string): string { + let packageYaml: {} = {}; + if (descriptorType === 'ns-package') { + packageYaml = { + 'nsd:nsd-catalog': { + nsd: [ + { + 'short-name': this.packagesForm.value.name, + vendor: 'OSM Composer', + description: this.packagesForm.value.name + ' descriptor', + vld: [], + 'constituent-vnfd': [], + version: '1.0', + id: this.packagesForm.value.name, + name: this.packagesForm.value.name + } + ] + } + }; + } else { + packageYaml = { + 'vnfd:vnfd-catalog': { + vnfd: [ + { + 'short-name': this.packagesForm.value.name, + vdu: [], + description: '', + 'mgmt-interface': { + cp: '' + }, + id: this.packagesForm.value.name, + version: '1.0', + 'internal-vld': [], + 'connection-point': [], + name: this.packagesForm.value.name + } + ] + } + }; + } + return jsyaml.dump(packageYaml); + } +} diff --git a/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.html b/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.html new file mode 100644 index 0000000..03f5564 --- /dev/null +++ b/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.html @@ -0,0 +1,81 @@ + +
+
+ + + +
+
+
+ + + +
\ No newline at end of file diff --git a/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.scss b/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.scss new file mode 100644 index 0000000..1ff404c --- /dev/null +++ b/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.scss @@ -0,0 +1,24 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +.help-key { + border: 1px solid #ddd; + padding: 4px; + border-radius: 3px; + background: #f6f6f6; + box-shadow: #999 1px 1px 1px; +} \ No newline at end of file diff --git a/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.ts b/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.ts new file mode 100644 index 0000000..a1a79b9 --- /dev/null +++ b/src/app/utilities/confirmation-topology/ConfirmationTopologyComponent.ts @@ -0,0 +1,102 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Delete Topology Model + */ +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { MODALCLOSERESPONSEWITHCP } from 'CommonModel'; +/** + * Creating component + * @Component takes ConfirmationTopologyComponent.html as template url + */ +@Component({ + selector: 'app-confirmation-topology', + templateUrl: './ConfirmationTopologyComponent.html', + styleUrls: ['./ConfirmationTopologyComponent.scss'] +}) +/** Exporting a class @exports ConfirmationTopologyComponent */ +export class ConfirmationTopologyComponent implements OnInit { + /** Form valid on submit trigger @public */ + public submitted: boolean = false; + /** To inject services @public */ + public injector: Injector; + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + /** FormGroup instance added to the form @ html @public */ + public addConfirmationForm: FormGroup; + /** Input contains Modal dialog componentInstance @private */ + @Input() public topologytitle: string; + /** Input contains Modal dialog componentInstance @private */ + @Input() public topologyname: string; + /** Input contains Modal dialog componentInstance @private */ + @Input() public topologyType: string; + /** Input contains Modal dialog componentInstance @private */ + @Input() public cpDetails: {}[]; + + /** Contains connectionpoint @public */ + public connectionPointInput: string; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + constructor(injector: Injector) { + this.injector = injector; + this.activeModal = this.injector.get(NgbActiveModal); + this.translateService = this.injector.get(TranslateService); + this.formBuilder = this.injector.get(FormBuilder); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.initializeForm(); + } + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.addConfirmationForm.controls; } + + /** initialize Forms @public */ + public initializeForm(): void { + this.addConfirmationForm = this.formBuilder.group({ + cpName: [null, [Validators.required]] + }); + } + + /** add confirmation to be handled in this function @public */ + public addConfirmation(): void { + this.submitted = true; + if (this.addConfirmationForm.invalid) { return; } // Proceed, onces form is valid + const modalData: MODALCLOSERESPONSEWITHCP = { + message: 'Done', + connection_point: this.connectionPointInput + }; + this.activeModal.close(modalData); + } + /** confirmation to be handled in this function @public */ + public confirmation(): void { + const modalData: MODALCLOSERESPONSEWITHCP = { + message: 'Done' + }; + this.activeModal.close(modalData); + } + +} diff --git a/src/app/utilities/delete/DeleteComponent.html b/src/app/utilities/delete/DeleteComponent.html new file mode 100644 index 0000000..a39cb61 --- /dev/null +++ b/src/app/utilities/delete/DeleteComponent.html @@ -0,0 +1,33 @@ + + + + + \ No newline at end of file diff --git a/src/app/utilities/delete/DeleteComponent.scss b/src/app/utilities/delete/DeleteComponent.scss new file mode 100644 index 0000000..cdd37f2 --- /dev/null +++ b/src/app/utilities/delete/DeleteComponent.scss @@ -0,0 +1,20 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +.loadermessage-column { + min-height: 150px; +} \ No newline at end of file diff --git a/src/app/utilities/delete/DeleteComponent.ts b/src/app/utilities/delete/DeleteComponent.ts new file mode 100644 index 0000000..4baee64 --- /dev/null +++ b/src/app/utilities/delete/DeleteComponent.ts @@ -0,0 +1,199 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Delete Model + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, Injector, Input } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { DELETEPARAMS, ERRORDATA, MODALCLOSERESPONSEDATA, URLPARAMS } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { RestService } from 'RestService'; + +/** + * Creating component + * @Component takes DeleteComponent.html as template url + */ +@Component({ + selector: 'app-delete', + templateUrl: './DeleteComponent.html', + styleUrls: ['./DeleteComponent.scss'] +}) +/** Exporting a class @exports DeleteComponent */ +export class DeleteComponent { + /** To inject services @public */ + public injector: Injector; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Instance of the modal service @public */ + public title: string; + + /** Show the Delete Ok button to trigger the terminate and delete */ + public forceDelete: boolean = false; + + /** Check the loading results @public */ + public isLoadingResults: Boolean = false; + + /** Give the message for the loading @public */ + public notifyMessage: string = 'DELETELOADERMESSAGE'; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** DataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Instance of the modal service @private */ + private id: string; + + /** Variables holds url to be delete @private */ + private deleteURL: string; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Input contains component objects @private */ + @Input() private params: URLPARAMS; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.activeModal = this.injector.get(NgbActiveModal); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.headers = new HttpHeaders({ + 'Content-Type': 'application/json', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + this.dataService.currentMessage.subscribe((data: DELETEPARAMS) => { + if (data.identifier !== undefined || data.identifier !== '' || data.identifier !== null) { + this.id = data.identifier; + } + this.createTitleandID(data); + this.createDeleteUrl(data); + }); + } + /** Generate Title and Id from data @public */ + public createTitleandID(data: DELETEPARAMS): void { + this.title = ''; + if (data.name !== undefined) { + this.title = data.name; + } else if (data.shortName !== undefined) { + this.title = data.shortName; + } else if (data.projectName !== undefined) { + this.title = data.projectName; + this.id = this.title; + } else if (data.userName !== undefined) { + this.title = data.userName; + } else if (data.username !== undefined) { + this.title = data.username; + } + } + /** Generate Delete url from data @public */ + public createDeleteUrl(data: DELETEPARAMS): void { + this.deleteURL = ''; + if (data.page === 'ns-instance') { + this.deleteURL = environment.NSINSTANCESCONTENT_URL; + this.forceDelete = this.params.forceDeleteType; + } else if (data.page === 'ns-package') { + this.deleteURL = environment.NSDESCRIPTORSCONTENT_URL; + this.notifyMessage = 'DELETEDSUCCESSFULLY'; + } else if (data.page === 'vnf-package') { + this.deleteURL = environment.VNFPACKAGESCONTENT_URL; + this.notifyMessage = 'DELETEDSUCCESSFULLY'; + } else if (data.page === 'vim-account') { + this.deleteURL = environment.VIMACCOUNTS_URL; + this.notifyMessage = 'DELETEDSUCCESSFULLY'; + } else if (data.page === 'wim-account') { + this.deleteURL = environment.WIMACCOUNTS_URL; + this.notifyMessage = 'DELETEDSUCCESSFULLY'; + } else if (data.page === 'projects') { + this.deleteURL = environment.PROJECTS_URL; + this.notifyMessage = 'DELETEDSUCCESSFULLY'; + this.id = data.id; + } else if (data.page === 'users') { + this.deleteURL = environment.USERS_URL; + this.notifyMessage = 'DELETEDSUCCESSFULLY'; + } else if (data.page === 'network-slice') { + this.deleteURL = environment.NETWORKSLICETEMPLATECONTENT_URL; + this.notifyMessage = 'DELETEDSUCCESSFULLY'; + } else if (data.page === 'net-slice-instance') { + this.deleteURL = environment.NETWORKSLICEINSTANCESCONTENT_URL; + this.forceDelete = this.params.forceDeleteType; + } else if (data.page === 'roles') { + this.deleteURL = environment.ROLES_URL; + this.notifyMessage = 'DELETEDSUCCESSFULLY'; + } else if (data.page === 'pdu-instances') { + this.deleteURL = environment.PDUINSTANCE_URL; + } else if (data.page === 'sdn-controller') { + this.deleteURL = environment.SDNCONTROLLER_URL; + this.notifyMessage = 'DELETEDSUCCESSFULLY'; + } else if (data.page === 'k8-cluster') { + this.deleteURL = environment.K8SCLUSTER_URL; + this.notifyMessage = 'DELETEDSUCCESSFULLY'; + } else if (data.page === 'k8-repo') { + this.deleteURL = environment.K8REPOS_URL; + this.notifyMessage = 'DELETEDSUCCESSFULLY'; + } + } + /** Generate Data function @public */ + public deleteData(): void { + this.isLoadingResults = true; + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + let deletingURl: string = ''; + if (this.forceDelete) { + deletingURl = this.deleteURL + '/' + this.id + '?FORCE=true'; + this.notifyMessage = 'DELETEDSUCCESSFULLY'; + } else { + deletingURl = this.deleteURL + '/' + this.id; + } + this.restService.deleteResource(deletingURl).subscribe((res: {}) => { + this.activeModal.close(modalData); + this.notifierService.notify('success', this.translateService.instant(this.notifyMessage, { title: this.title})); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'delete'); + }, () => { + this.isLoadingResults = false; + }); + } +} diff --git a/src/app/utilities/dragDropUpload/DragDirective.ts b/src/app/utilities/dragDropUpload/DragDirective.ts new file mode 100644 index 0000000..735ac7c --- /dev/null +++ b/src/app/utilities/dragDropUpload/DragDirective.ts @@ -0,0 +1,87 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Drag and Drop feature. + */ +import { Directive, EventEmitter, HostBinding, HostListener, Output } from '@angular/core'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; + +/** Interface for FileHandle */ +export interface FileHandle { + file: File; + url: SafeUrl; +} + +/** + * Creating Directive + * @Directive for handling the files. + */ +// tslint:disable-next-line:export-name +@Directive({ + selector: '[appDrag]' +}) +/** Exporting a class @exports DragDirective */ +export class DragDirective { + /** To publish the details of files @public */ + @Output() public files: EventEmitter = new EventEmitter(); + + /** To set the background of drag and drop region @public */ + @HostBinding('style.background') private background: string = '#e6f3fe'; + + /** To set the background of drag and drop region @public */ + @HostBinding('style.color') private color: string = '#6a7a8c'; + + /** To trust the SecurityURL @public */ + private sanitizer: DomSanitizer; + + constructor(sanitizer: DomSanitizer) { + this.sanitizer = sanitizer; + } + + /** To handle the Drag over Event @public */ + @HostListener('dragover', ['$event']) public onDragOver(evt: DragEvent): void { + evt.preventDefault(); + evt.stopPropagation(); + this.background = '#087add'; + this.color = '#fff'; + } + /** To handle Drag leave Event @public */ + @HostListener('dragleave', ['$event']) public onDragLeave(evt: DragEvent): void { + evt.preventDefault(); + evt.stopPropagation(); + this.background = '#e6f3fe'; + this.color = '#6a7a8c'; + } + /** To handle Drop Event @public */ + @HostListener('drop', ['$event']) public onDrop(evt: DragEvent): void { + evt.preventDefault(); + evt.stopPropagation(); + this.background = '#e6f3fe'; + this.color = '#6a7a8c'; + + const files: FileHandle[] = []; + Array.from(evt.dataTransfer.files).forEach((listFiles: File, index: number) => { + const file: File = listFiles; + const url: SafeUrl = this.sanitizer.bypassSecurityTrustUrl(window.URL.createObjectURL(file)); + files.push({ file, url }); + }); + if (files.length > 0) { + this.files.emit(evt.dataTransfer.files); + } + } +} diff --git a/src/app/utilities/edit-packages/EditPackagesComponent.html b/src/app/utilities/edit-packages/EditPackagesComponent.html new file mode 100644 index 0000000..2defc33 --- /dev/null +++ b/src/app/utilities/edit-packages/EditPackagesComponent.html @@ -0,0 +1,61 @@ + +
+
{{'EDIT' | translate}} {{pacakgeType}} {{'DESCRIPTOR' | translate}}
+
+
+
+
+
+ +
+
+
+
+ +
+
+
+ + + +
+
+
+
+ +
+ + {{'NODATAERROR' | translate}} + + \ No newline at end of file diff --git a/src/app/utilities/edit-packages/EditPackagesComponent.scss b/src/app/utilities/edit-packages/EditPackagesComponent.scss new file mode 100644 index 0000000..5a9c483 --- /dev/null +++ b/src/app/utilities/edit-packages/EditPackagesComponent.scss @@ -0,0 +1,20 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +.ngx-codemirror { + font-size: 14px; +} \ No newline at end of file diff --git a/src/app/utilities/edit-packages/EditPackagesComponent.ts b/src/app/utilities/edit-packages/EditPackagesComponent.ts new file mode 100644 index 0000000..befafb8 --- /dev/null +++ b/src/app/utilities/edit-packages/EditPackagesComponent.ts @@ -0,0 +1,318 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Edit Actions Component + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, Injector, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import 'codemirror/addon/dialog/dialog'; +import 'codemirror/addon/display/autorefresh'; +import 'codemirror/addon/display/fullscreen'; +import 'codemirror/addon/edit/closebrackets'; +import 'codemirror/addon/edit/matchbrackets'; +import 'codemirror/addon/fold/brace-fold'; +import 'codemirror/addon/fold/foldcode'; +import 'codemirror/addon/fold/foldgutter'; +import 'codemirror/addon/search/search'; +import 'codemirror/addon/search/searchcursor'; +import 'codemirror/keymap/sublime'; +import 'codemirror/lib/codemirror'; +import 'codemirror/mode/javascript/javascript'; +import 'codemirror/mode/markdown/markdown'; +import 'codemirror/mode/yaml/yaml'; +import { APIURLHEADER, ERRORDATA, GETAPIURLHEADER } from 'CommonModel'; +import { environment } from 'environment'; +import * as HttpStatus from 'http-status-codes'; +import * as jsyaml from 'js-yaml'; +import { NSDDetails } from 'NSDModel'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes EditPackagesComponent.html as template url + */ +@Component({ + selector: 'app-edit-packages', + templateUrl: './EditPackagesComponent.html', + styleUrls: ['./EditPackagesComponent.scss'] +}) + +/** Exporting a class @exports EditPackagesComponent */ +export class EditPackagesComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** dataService to pass the data from one component to another @public */ + public identifier: {} = {}; + + /** readOnly @public */ + public readOnly: boolean = false; + + /** Handle the formate Change @public */ + public defaults: {} = { + 'text/x-yaml': '', + 'text/json': '' + }; + + /** Get & Update URL VNFD & NSD */ + public getUpdateURL: string; + + /** Pass the type of VNFD & NSD for fetching text */ + public getFileContentType: string; + + /** Pass the type of VNFD & NSD for fileUpdate */ + public updateFileContentType: string; + + /** To Set Mode @public */ + public mode: string = 'text/x-yaml'; + + /** To Set Mode @public */ + public modeDefault: string = 'yaml'; + + /** options @public */ + public options: {} = { + mode: this.modeDefault, + showCursorWhenSelecting: true, + autofocus: true, + autoRefresh: true, + lineNumbers: true, + lineWrapping: true, + foldGutter: true, + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], + autoCloseBrackets: true, + matchBrackets: true, + theme: 'neat', + keyMap: 'sublime' + }; + + /** Ymal Url for the VNFD & NSD */ + public ymalUrl: string; + + /** json Url for the VNFD & NSD */ + public jsonUrl: string; + + /** Navigation Path for the VNFD & NSD */ + public navigatePath: string; + + /** Package type */ + public pacakgeType: string; + + /** variables contains paramsID @public */ + public paramsID: string; + + /** Controls the File Type List form @public */ + public fileTypes: { value: string; viewValue: string; }[] = []; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private activatedRoute: ActivatedRoute; + + /** Data @private */ + private data: string = ''; + + /** contains http options @private */ + private httpOptions: HttpHeaders; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.activatedRoute = this.injector.get(ActivatedRoute); + this.router = this.injector.get(Router); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.headers = new HttpHeaders({ + 'Content-Type': 'application/json', + Accept: 'text/plain', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.paramsID = this.activatedRoute.snapshot.paramMap.get('id'); + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.pacakgeType = this.activatedRoute.snapshot.paramMap.get('type'); + this.generateURLPath(); + } + + /** generate ymalURL, JSONURL, navigation Path */ + public generateURLPath(): void { + if (this.pacakgeType === 'vnf') { + this.getUpdateURL = environment.VNFPACKAGES_URL; + this.getFileContentType = 'vnfd'; + this.updateFileContentType = 'package_content'; + this.navigatePath = 'vnf'; + this.fileTypes = [{ value: 'text/x-yaml', viewValue: 'yaml' }, { value: 'text/json', viewValue: 'json' }]; + this.httpOptions = this.getHeadersWithContentAccept('application/zip', 'application/json'); + this.getEditFileData(); + } else if (this.pacakgeType === 'netslice') { + this.getUpdateURL = environment.NETWORKSLICETEMPLATE_URL; + this.getFileContentType = 'nst'; + this.updateFileContentType = 'nst_content'; + this.navigatePath = 'netslice'; + this.fileTypes = [{ value: 'text/x-yaml', viewValue: 'yaml' }]; + this.httpOptions = this.getHeadersWithContentAccept('application/yaml', 'application/json'); + this.getEditFileData(); + } else { + this.getUpdateURL = environment.NSDESCRIPTORS_URL; + this.getFileContentType = 'nsd'; + this.updateFileContentType = 'nsd_content'; + this.pacakgeType = 'nsd'; + this.navigatePath = 'ns'; + this.fileTypes = [{ value: 'text/x-yaml', viewValue: 'yaml' }, { value: 'text/json', viewValue: 'json' }]; + this.httpOptions = this.getHeadersWithContentAccept('application/zip', 'application/json'); + this.getEditFileData(); + } + } + + /** Get the headers based on the type @public */ + public getHeadersWithContentAccept(contentType: string, acceptType: string): HttpHeaders { + this.headers = new HttpHeaders({ + 'Content-Type': contentType, + Accept: acceptType, + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + return this.headers; + } + + /** ChangeMode function @public */ + public changeMode(): void { + if (this.mode === 'text/x-yaml') { + this.modeDefault = 'yaml'; + } else { + this.modeDefault = 'javascript'; + } + this.options = { + ...this.options, + mode: this.modeDefault + }; + this.data = ''; + } + + /** HandleChange function @public */ + public handleChange($event: string): void { + this.data = $event; + } + + /** Update function @public */ + public update(showgraph: boolean): void { + if (this.data === '') { + this.notifierService.notify('warning', this.translateService.instant('PAGE.TOPOLOGY.DATAEMPTY')); + } else { + this.updateCheck(showgraph); + } + } + /** Update the file Data @public */ + public updateFileData(urlHeader: APIURLHEADER, fileData: string | ArrayBuffer, showgraph: boolean, packageType: string): void { + this.restService.putResource(urlHeader, fileData).subscribe(() => { + this.isLoadingResults = false; + this.notifierService.notify('success', this.translateService.instant( + (packageType !== 'netslice') ? 'PAGE.NSPACKAGE.EDITPACKAGES.UPDATEDSUCCESSFULLY' : 'PAGE.NETSLICE.UPDATEDSUCCESSFULLY')); + if (showgraph) { + if (packageType === 'nsd') { + this.router.navigate(['/packages/ns/compose/' + this.paramsID]).catch(); + } else if (packageType === 'vnf') { + this.router.navigate(['/packages/vnf/compose/' + this.paramsID]).catch(); + } + } + this.getEditFileData(); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'put'); + }); + } + /** Update method for NS, VNF and net-slice template */ + private updateCheck(showgraph: boolean): void { + this.isLoadingResults = true; + const apiURLHeader: APIURLHEADER = { + url: this.getUpdateURL + '/' + this.paramsID + '/' + this.updateFileContentType, + httpOptions: { headers: this.httpOptions } + }; + let descriptorInfo: string = ''; + if (this.mode === 'text/json') { + descriptorInfo = jsyaml.dump(JSON.parse(this.data), {sortKeys: true}); + } else { + descriptorInfo = this.data; + } + if (this.getFileContentType !== 'nst') { + this.sharedService.targzFile({ packageType: this.pacakgeType, id: this.paramsID, descriptor: descriptorInfo }) + .then((content: ArrayBuffer): void => { + this.updateFileData(apiURLHeader, content, showgraph, this.pacakgeType); + }).catch((): void => { + this.isLoadingResults = false; + this.notifierService.notify('error', this.translateService.instant('ERROR')); + }); + } else { + this.updateFileData(apiURLHeader, descriptorInfo, showgraph, this.pacakgeType); + } + } + /** Get the YAML content response as a plain/text and convert to JSON Format @private */ + private getEditFileData(): void { + this.isLoadingResults = true; + const gethttpOptions: HttpHeaders = this.getHeadersWithContentAccept('application/json', 'text/plain'); + const httpOptions: GETAPIURLHEADER = { + headers: gethttpOptions, + responseType: 'text' + }; + this.restService.getResource(this.getUpdateURL + '/' + this.paramsID + '/' + this.getFileContentType, httpOptions) + .subscribe((nsData: NSDDetails[]) => { + const getJson: string = jsyaml.load(nsData.toString(), { json: true }); + //tslint:disable-next-line:no-string-literal + this.defaults['text/x-yaml'] = nsData.toString(); + this.defaults['text/json'] = JSON.stringify(getJson, null, '\t'); + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + error.error = typeof error.error === 'string' ? jsyaml.load(error.error) : error.error; + if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED ) { + this.router.navigateByUrl('404', { skipLocationChange: true }).catch(); + } else { + this.restService.handleError(error, 'get'); + } + this.isLoadingResults = false; + }); + } +} diff --git a/src/app/utilities/loader/LoaderComponent.html b/src/app/utilities/loader/LoaderComponent.html new file mode 100644 index 0000000..30ce264 --- /dev/null +++ b/src/app/utilities/loader/LoaderComponent.html @@ -0,0 +1,27 @@ + +
+
+
{{'LOADING' | translate}}...
+

+ {{getMessage | translate}} + . + +

+
+
\ No newline at end of file diff --git a/src/app/utilities/loader/LoaderComponent.scss b/src/app/utilities/loader/LoaderComponent.scss new file mode 100644 index 0000000..6bfd321 --- /dev/null +++ b/src/app/utilities/loader/LoaderComponent.scss @@ -0,0 +1,89 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +@import '../../../assets/scss/mixins/mixin'; +@import '../../../assets/scss/variable'; +.loader-overlay { + -ms-opacity: 0.9; + @include background(null, $white, null, null, null); + @include position_value(absolute, 0, null, null, 0); + @include wh-value(100%, 100%); + opacity: 0.9; + vertical-align: middle; + z-index: 100000; + .loader-content { + @include wh-value(50%, null); + margin-left: auto; + margin-top: auto; + .loader-text { + color: $body-color; + } + } + .loader-center { + @include position_value(absolute, 50%, null, null, 50%); + @include flexbox(block, null, null, null, null, null); + -moz-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + -o-transform: translate(-50%, -50%); + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -55%); + } +} +.loader, +.loader:after { + @include roundedCornersPercentage(50%); + @include wh-value(10em, 10em); +} +.loader { + @include font(null, 3px, null); + @include position_value(relative, null, null, null, null); + @include border(top, 2, solid, rgba(5, 76, 140, 0.28)); + @include border(right, 2, solid, rgba(5, 76, 140, 0.28)); + @include border(bottom, 2, solid, rgba(5, 76, 140, 0.28)); + @include border(left, 2, solid, $primary); + margin: 0 auto; + text-indent: -9999em; + -webkit-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); + -webkit-animation: load8 1.1s infinite linear; + animation: load8 1.1s infinite linear; +} +@-webkit-keyframes load8 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes load8 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes blink {50% { color: transparent }} +.loader__dot { animation: 1s blink infinite } +.loader__dot:nth-child(2) { animation-delay: 250ms } +.loader__dot:nth-child(3) { animation-delay: 500ms } \ No newline at end of file diff --git a/src/app/utilities/loader/LoaderComponent.ts b/src/app/utilities/loader/LoaderComponent.ts new file mode 100644 index 0000000..ee8cab8 --- /dev/null +++ b/src/app/utilities/loader/LoaderComponent.ts @@ -0,0 +1,50 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Delete Model + */ +import { Component, Input, OnInit } from '@angular/core'; +/** + * Creating component + * @Component takes LoaderComponent.html as template url + */ +@Component({ + selector: 'app-loader', + templateUrl: './LoaderComponent.html', + styleUrls: ['./LoaderComponent.scss'] +}) +/** Exporting a class @exports LoaderComponent */ +export class LoaderComponent implements OnInit { + /** Variables declared to get the message from parents @public */ + @Input() public waitingMessage: string; + /** Variables declared to get the message of loader @public */ + public getMessage: string; + + constructor() { + // Empty block + } + + public ngOnInit(): void { + if (this.waitingMessage !== '') { + this.getMessage = this.waitingMessage; + } else { + this.getMessage = ''; + } + } + +} diff --git a/src/app/utilities/loader/LoaderModule.ts b/src/app/utilities/loader/LoaderModule.ts new file mode 100644 index 0000000..ba81b6a --- /dev/null +++ b/src/app/utilities/loader/LoaderModule.ts @@ -0,0 +1,41 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Loader Module. + */ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { LoaderComponent } from 'LoaderComponent'; +/** + * Creating @NgModule component for Modules + */ +@NgModule({ + imports: [CommonModule, TranslateModule], + declarations: [LoaderComponent], + exports: [LoaderComponent] +}) +/** Exporting a class @exports LoaderModule */ +export class LoaderModule { + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + // Empty Block + } +} diff --git a/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.html b/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.html new file mode 100644 index 0000000..7fcc812 --- /dev/null +++ b/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.html @@ -0,0 +1,42 @@ + +
+ + +
+ + +
+
\ No newline at end of file diff --git a/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.scss b/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.ts b/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.ts new file mode 100644 index 0000000..0e528a8 --- /dev/null +++ b/src/app/utilities/netslice-instances-action/NetsliceInstancesActionComponent.ts @@ -0,0 +1,95 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Netslice InstancesAction Component + */ +import { Component, Injector } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { NSTInstanceData } from 'NetworkSliceModel'; +import { SharedService } from 'SharedService'; +import { ShowInfoComponent } from 'ShowInfoComponent'; +/** + * Creating component + * @Component takes NetsliceInstancesActionComponent.html as template url + */ +@Component({ + templateUrl: './NetsliceInstancesActionComponent.html', + styleUrls: ['./NetsliceInstancesActionComponent.scss'] +}) +/** Exporting a class @exports NetsliceInstancesActionComponent */ +export class NetsliceInstancesActionComponent { + /** To get the value from the vnfpackage via valuePrepareFunction default Property of ng-smarttable @public */ + public value: NSTInstanceData; + + /** To inject services @public */ + public injector: Injector; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Contains instance ID @private */ + private instanceID: string; + + /** Service holds the router information @private */ + private router: Router; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.modalService = this.injector.get(NgbModal); + this.router = this.injector.get(Router); + this.sharedService = this.injector.get(SharedService); + } + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.instanceID = this.value.identifier; + } + + /** Shows information using modalservice @public */ + public infoNetSliceInstance(): void { + this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = { + id: this.instanceID, + page: 'net-slice-instance', + titleName: 'PAGE.NETSLICETEMPLATE.NETSLICETEMPLATEDETAILS' + }; + } + + /** Delete NetSlice Instance packages @public */ + public deleteNetSliceInstance(forceAction: boolean): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' }); + modalRef.componentInstance.params = {forceDeleteType: forceAction}; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + /** History of operations for an Instanace @public */ + public historyOfOperations(): void { + this.router.navigate(['/instances/netslice/history-operations/', this.instanceID]).catch(() => { + // Catch Navigation Error + }); + } +} diff --git a/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.html b/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.html new file mode 100644 index 0000000..d47d65e --- /dev/null +++ b/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.html @@ -0,0 +1,34 @@ + +
+ + + + +
\ No newline at end of file diff --git a/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.scss b/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.ts b/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.ts new file mode 100644 index 0000000..8be5f50 --- /dev/null +++ b/src/app/utilities/netslice-packages-action/NetslicePackagesActionComponent.ts @@ -0,0 +1,102 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Netslice-packagesAction Component + */ +import { Component, Injector } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { InstantiateNetSliceTemplateComponent } from 'InstantiateNetSliceTemplate'; +import { NetworkSliceData } from 'NetworkSliceModel'; +import { SharedService } from 'SharedService'; +import { ShowInfoComponent } from 'ShowInfoComponent'; + +/** + * Creating component + * @Component takes NetslicePackagesActionComponent.html as template url + */ +@Component({ + selector: 'app-netslice-packages-action', + templateUrl: './NetslicePackagesActionComponent.html', + styleUrls: ['./NetslicePackagesActionComponent.scss'] +}) +/** Exporting a class @exports NetslicePackagesActionComponent */ +export class NetslicePackagesActionComponent { + /** To get the value from the vnfpackage via valuePrepareFunction default Property of ng-smarttable @public */ + public value: NetworkSliceData; + + /** To inject services @public */ + public injector: Injector; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Contains instance ID @private */ + private instanceID: string; + + /** Service holds the router information @private */ + private router: Router; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.modalService = this.injector.get(NgbModal); + this.router = this.injector.get(Router); + this.sharedService = this.injector.get(SharedService); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.instanceID = this.value.identifier; + } + + /** Delete NetSliceTemplate packages @public */ + public deleteNetSliceTemplate(): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, {backdrop: 'static'}); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** Shows information using modalservice @public */ + public infoNetSlice(): void { + this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = { + id: this.instanceID, + page: 'net-slice-package', + titleName: 'PAGE.NETSLICETEMPLATE.NETSLICETEMPLATEDETAILS' + }; + } + + /** Set Edit for the @public */ + public netSliceEdit(): void { + this.router.navigate(['/packages/netslice/edit/', this.instanceID]).catch(() => { + // Catch Navigation Error + }); + } + + /** Instantiate Net Slice using modalservice @public */ + public instantiateNetSlice(): void { + this.modalService.open(InstantiateNetSliceTemplateComponent, { backdrop: 'static' }); + } +} diff --git a/src/app/utilities/ns-instances-action/NSInstancesActionComponent.html b/src/app/utilities/ns-instances-action/NSInstancesActionComponent.html new file mode 100644 index 0000000..41a58f5 --- /dev/null +++ b/src/app/utilities/ns-instances-action/NSInstancesActionComponent.html @@ -0,0 +1,56 @@ + +
+ + + +
+ + +
+
+ \ No newline at end of file diff --git a/src/app/utilities/ns-instances-action/NSInstancesActionComponent.scss b/src/app/utilities/ns-instances-action/NSInstancesActionComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/ns-instances-action/NSInstancesActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/ns-instances-action/NSInstancesActionComponent.ts b/src/app/utilities/ns-instances-action/NSInstancesActionComponent.ts new file mode 100644 index 0000000..8bf43ce --- /dev/null +++ b/src/app/utilities/ns-instances-action/NSInstancesActionComponent.ts @@ -0,0 +1,181 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file NS InstancesAction Component + */ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { ERRORDATA, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { environment } from 'environment'; +import { NSDDetails } from 'NSDModel'; +import { NSDInstanceData } from 'NSInstanceModel'; +import { NSPrimitiveComponent } from 'NSPrimitiveComponent'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { ShowInfoComponent } from 'ShowInfoComponent'; +/** + * Creating component + * @Component takes NSInstancesActionComponent.html as template url + */ +@Component({ + templateUrl: './NSInstancesActionComponent.html', + styleUrls: ['./NSInstancesActionComponent.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +/** Exporting a class @exports NSInstancesActionComponent */ +export class NSInstancesActionComponent { + /** To get the value from the nspackage via valuePrepareFunction default Property of ng-smarttable @public */ + public value: NSDInstanceData; + + /** Invoke service injectors @public */ + public injector: Injector; + + /** Instance of the modal service @public */ + public restService: RestService; + + /** Config Status Check @public */ + public configStatus: string; + + /** Operational Status Check @public */ + public operationalStatus: string; + + /** Check the loading results for loader status @public */ + public isLoadingMetricsResult: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + /** Contains instance ID @private */ + private instanceID: string; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Detect changes for the User Input */ + private cd: ChangeDetectorRef; + + /** Set timeout @private */ + private timeOut: number = 1000; + + constructor(injector: Injector) { + this.injector = injector; + this.modalService = this.injector.get(NgbModal); + this.restService = this.injector.get(RestService); + this.router = this.injector.get(Router); + this.sharedService = this.injector.get(SharedService); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.cd = this.injector.get(ChangeDetectorRef); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.configStatus = this.value.ConfigStatus; + this.operationalStatus = this.value.OperationalStatus; + this.instanceID = this.value.identifier; + } + + /** Shows information using modalservice @public */ + public infoNs(): void { + this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = { + id: this.instanceID, + page: 'ns-instance', + titleName: 'INSTANCEDETAILS' + }; + } + + /** Delete NS Instanace @public */ + public deleteNSInstance(forceAction: boolean): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' }); + modalRef.componentInstance.params = { forceDeleteType: forceAction }; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** History of operations for an Instanace @public */ + public historyOfOperations(): void { + this.router.navigate(['/instances/ns/history-operations/', this.instanceID]).catch(() => { + // Catch Navigation Error + }); + } + + /** NS Topology */ + public nsTopology(): void { + this.router.navigate(['/instances/ns/', this.instanceID]).catch(() => { + // Catch Navigation Error + }); + } + + /** Exec NS Primitive @public */ + public execNSPrimitiveModal(): void { + this.modalService.open(NSPrimitiveComponent).componentInstance.params = { + memberIndex: this.value.memberIndex, + nsConfig: this.value.nsConfig + }; + } + + /** Redirect to Grafana Metrics @public */ + public metrics(): void { + this.isLoadingMetricsResult = true; + this.restService.getResource(environment.NSDINSTANCES_URL + '/' + this.instanceID).subscribe((nsData: NSDDetails[]) => { + nsData['vnfd-id'].forEach((vnfdID: string[]) => { + this.restService.getResource(environment.VNFPACKAGES_URL + '/' + vnfdID) + .subscribe((vnfd: {}[]) => { + if (vnfd['monitoring-param'] !== undefined && vnfd['monitoring-param'].length > 0) { + this.isLoadingMetricsResult = false; + const location: string = environment.GRAFANA_URL + '/' + this.instanceID + '/osm-ns-metrics-metrics'; + window.open(location); + } else { + this.isLoadingMetricsResult = false; + this.notifierService.notify('error', this.translateService.instant('GRAFANA.METRICSERROR')); + } + setTimeout(() => { + this.cd.detectChanges(); + }, this.timeOut); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingMetricsResult = false; + }); + }); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingMetricsResult = false; + }); + } +} diff --git a/src/app/utilities/ns-packages-action/NsPackagesActionComponent.html b/src/app/utilities/ns-packages-action/NsPackagesActionComponent.html new file mode 100644 index 0000000..6e33d0d --- /dev/null +++ b/src/app/utilities/ns-packages-action/NsPackagesActionComponent.html @@ -0,0 +1,55 @@ + +
+ + + +
+ + +
+
+ \ No newline at end of file diff --git a/src/app/utilities/ns-packages-action/NsPackagesActionComponent.scss b/src/app/utilities/ns-packages-action/NsPackagesActionComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/ns-packages-action/NsPackagesActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/ns-packages-action/NsPackagesActionComponent.ts b/src/app/utilities/ns-packages-action/NsPackagesActionComponent.ts new file mode 100644 index 0000000..6e7fbfb --- /dev/null +++ b/src/app/utilities/ns-packages-action/NsPackagesActionComponent.ts @@ -0,0 +1,193 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file NS PackagesAction Component + */ +import { HttpHeaders } from '@angular/common/http'; +import { ChangeDetectorRef, Component, Injector } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { ClonePackageComponent } from 'ClonePackage'; +import { ERRORDATA, GETAPIURLHEADER, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { environment } from 'environment'; +import { InstantiateNsComponent } from 'InstantiateNs'; +import { NSData } from 'NSDModel'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { ShowContentComponent } from 'ShowContent'; + +/** + * Creating component + * @Component takes NsPackagesActionComponent.html as template url + */ +@Component({ + selector: 'app-ns-packages-action', + templateUrl: './NsPackagesActionComponent.html', + styleUrls: ['./NsPackagesActionComponent.scss'] +}) + +/** Exporting a class @exports NsPackagesActionComponent */ +export class NsPackagesActionComponent { + /** To get the value from the nspackage via valuePrepareFunction default Property of ng-smarttable @public */ + public value: NSData; + + /** To inject services @public */ + public injector: Injector; + + /** Check the loading results for loader status @public */ + public isLoadingDownloadResult: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Variables holds NS ID @private */ + private nsdID: string; + + /** Variables holds NS name @private */ + private nsdName: string; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Detect changes for the User Input */ + private cd: ChangeDetectorRef; + + /** Set timeout @private */ + private timeOut: number = 1000; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.sharedService = this.injector.get(SharedService); + this.modalService = this.injector.get(NgbModal); + this.router = this.injector.get(Router); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.cd = this.injector.get(ChangeDetectorRef); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.headers = new HttpHeaders({ + Accept: 'application/zip, application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + this.nsdID = this.value.identifier; + this.nsdName = this.value.shortName; + } + + /** Instantiate NS using modalservice @public */ + public instantiateNS(): void { + this.modalService.open(InstantiateNsComponent, { backdrop: 'static' }); + } + + /** Delete NS Package @public */ + public deleteNSPackage(): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** Set instance for NSD Edit @public */ + public nsdEdit(): void { + this.router.navigate(['/packages/ns/edit/', this.nsdID]).catch(() => { + // Catch Navigation Error + }); + } + + /** list out all the file content of a descriptors @public */ + public showContent(): void { + this.modalService.open(ShowContentComponent, { backdrop: 'static' }).componentInstance.params = { id: this.nsdID, page: 'nsd' }; + } + + /** Download NS Package @public */ + public downloadNSPackage(): void { + this.isLoadingDownloadResult = true; + const httpOptions: GETAPIURLHEADER = { + headers: this.headers, + responseType: 'blob' + }; + this.restService.getResource(environment.NSDESCRIPTORS_URL + '/' + this.nsdID + '/nsd_content', httpOptions) + .subscribe((response: Blob) => { + const binaryData: Blob[] = []; + binaryData.push(response); + this.sharedService.downloadFiles(this.nsdName, binaryData, response.type); + this.isLoadingDownloadResult = false; + this.changeDetactionforDownload(); + }, (error: ERRORDATA) => { + this.isLoadingDownloadResult = false; + this.notifierService.notify('error', this.translateService.instant('ERROR')); + this.changeDetactionforDownload(); + if (typeof error.error === 'object') { + error.error.text().then((data: string): void => { + error.error = JSON.parse(data); + this.restService.handleError(error, 'getBlob'); + }); + } + }); + } + + /** Compose NS Packages @public */ + public composeNSPackages(): void { + this.router.navigate(['/packages/ns/compose/', this.nsdID]).catch(() => { + // Catch Navigation Error + }); + } + + /** Change the detaction @public */ + public changeDetactionforDownload(): void { + setTimeout(() => { + this.cd.detectChanges(); + }, this.timeOut); + } + + /** Clone NS Packages @public */ + public cloneNSPackage(): void { + const cloneModal: NgbModalRef = this.modalService.open(ClonePackageComponent, { backdrop: 'static' }); + cloneModal.componentInstance.params = { id: this.nsdID, page: 'nsd', name: this.nsdName }; + cloneModal.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } +} diff --git a/src/app/utilities/page-per-row/PagePerRow.html b/src/app/utilities/page-per-row/PagePerRow.html new file mode 100644 index 0000000..a63ebb4 --- /dev/null +++ b/src/app/utilities/page-per-row/PagePerRow.html @@ -0,0 +1,26 @@ + +
+ + +
\ No newline at end of file diff --git a/src/app/utilities/page-per-row/PagePerRow.scss b/src/app/utilities/page-per-row/PagePerRow.scss new file mode 100644 index 0000000..06c069e --- /dev/null +++ b/src/app/utilities/page-per-row/PagePerRow.scss @@ -0,0 +1,23 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +.page-per-row { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +} \ No newline at end of file diff --git a/src/app/utilities/page-per-row/PagePerRow.ts b/src/app/utilities/page-per-row/PagePerRow.ts new file mode 100644 index 0000000..6794bfc --- /dev/null +++ b/src/app/utilities/page-per-row/PagePerRow.ts @@ -0,0 +1,80 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ + +/** + * @file PagePerRow Model + */ +import { Component, EventEmitter, Injector, Output } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { PAGERSMARTTABLE } from 'CommonModel'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes PagePerRow.html as template url + */ +@Component({ + selector: 'page-per-row', + templateUrl: './PagePerRow.html', + styleUrls: ['./PagePerRow.scss'] +}) +/** Exporting a class @exports PagePerRow */ +export class PagePerRow { + /** To inject services @public */ + public injector: Injector; + + /** handle translate @public */ + public translateService: TranslateService; + + /** get the pagaintion default select value @public */ + public getDefaultSelected: number; + + /** Controls the pagination List Count form @public */ + public pageCount: { value: number; viewValue: number; }[] = + [ + { value: 10, viewValue: 10 }, + { value: 25, viewValue: 25 }, + { value: 50, viewValue: 50 }, + { value: 100, viewValue: 100 } + ]; + + /** Contains all methods related to shared @private */ + public sharedService: SharedService; + + /** Event emitter to emit selected page number @public */ + @Output() public pagePerRow: EventEmitter = new EventEmitter(); + + constructor(injector: Injector) { + this.injector = injector; + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + const getPaginationValues: PAGERSMARTTABLE = this.sharedService.paginationPagerConfig(); + this.getDefaultSelected = getPaginationValues.perPage; + } + + /** Handles select event @public */ + public onSelectRow(e: number): void { + this.pagePerRow.emit(e); + } +} diff --git a/src/app/utilities/page-per-row/PagePerRowModule.ts b/src/app/utilities/page-per-row/PagePerRowModule.ts new file mode 100644 index 0000000..e5b8a29 --- /dev/null +++ b/src/app/utilities/page-per-row/PagePerRowModule.ts @@ -0,0 +1,42 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Page Per Row Module. + */ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { PagePerRow } from 'PagePerRow'; + +/** + * Creating @NgModule component for Modules + */ +@NgModule({ + imports: [CommonModule, TranslateModule], + declarations: [PagePerRow], + exports: [PagePerRow] +}) +/** Exporting a class @exports PagePerRowModule */ +export class PagePerRowModule { + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + // Empty Block + } +} diff --git a/src/app/utilities/page-reload/PageReload.html b/src/app/utilities/page-reload/PageReload.html new file mode 100644 index 0000000..7d03e52 --- /dev/null +++ b/src/app/utilities/page-reload/PageReload.html @@ -0,0 +1,21 @@ + + \ No newline at end of file diff --git a/src/app/utilities/page-reload/PageReload.scss b/src/app/utilities/page-reload/PageReload.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/page-reload/PageReload.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/page-reload/PageReload.ts b/src/app/utilities/page-reload/PageReload.ts new file mode 100644 index 0000000..0ea3c3c --- /dev/null +++ b/src/app/utilities/page-reload/PageReload.ts @@ -0,0 +1,61 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Page Reload Component + */ +import { Component, Injector } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { SharedService } from 'SharedService'; +/** + * Creating component + * @Component takes PageReload.html as template url + */ +@Component({ + selector: 'page-reload', + templateUrl: './PageReload.html', + styleUrls: ['./PageReload.scss'] +}) +/** Exporting a class @exports PageReload */ +export class PageReload { + /** To inject services @public */ + public injector: Injector; + + /** handle translate @public */ + public translateService: TranslateService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + // Empty Block + } + + /** Handles select event @public */ + public reloadPage(): void { + this.sharedService.dataEvent.emit(); + } +} diff --git a/src/app/utilities/page-reload/PageReloadModule.ts b/src/app/utilities/page-reload/PageReloadModule.ts new file mode 100644 index 0000000..56c35fd --- /dev/null +++ b/src/app/utilities/page-reload/PageReloadModule.ts @@ -0,0 +1,43 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Page Per Row Module. + */ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateModule } from '@ngx-translate/core'; +import { PageReload } from 'PageReload'; + +/** + * Creating @NgModule component for Modules + */ +@NgModule({ + imports: [CommonModule, TranslateModule, NgbModule], + declarations: [PageReload], + exports: [PageReload] +}) +/** Exporting a class @exports PageReloadModule */ +export class PageReloadModule { + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + // Empty Block + } +} diff --git a/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.html b/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.html new file mode 100644 index 0000000..351cf9a --- /dev/null +++ b/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.html @@ -0,0 +1,25 @@ + +
+ + +
\ No newline at end of file diff --git a/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.scss b/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.ts b/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.ts new file mode 100644 index 0000000..8cfb6cf --- /dev/null +++ b/src/app/utilities/pdu-instances-action/PDUInstancesActionComponent.ts @@ -0,0 +1,88 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file PDUInstancesActionComponent Component + */ +import { Component, Injector } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { PDUInstanceDetails } from 'PDUInstanceModel'; +import { SharedService } from 'SharedService'; +import { ShowInfoComponent } from 'ShowInfoComponent'; + +/** + * Creating component + * @Component takes PDUInstancesActionComponent.html as template url + */ +@Component({ + templateUrl: './PDUInstancesActionComponent.html', + styleUrls: ['./PDUInstancesActionComponent.scss'] +}) +/** Exporting a class @exports PDUInstancesActionComponent */ +export class PDUInstancesActionComponent { + /** To get the value from the PDU Instances via valuePrepareFunction default Property of ng-smarttable @public */ + public value: PDUInstanceDetails; + + /** To inject services @public */ + public injector: Injector; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Contains PDU Instance ID @private */ + private pduInstanceID: string; + + /** Service holds the router information @private */ + private router: Router; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.modalService = this.injector.get(NgbModal); + this.router = this.injector.get(Router); + this.sharedService = this.injector.get(SharedService); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.pduInstanceID = this.value.identifier; + } + + /** Delete PDU Instances @public */ + public deletePDUInstance(): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, {backdrop: 'static'}); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** Shows PDU Instances information using modalservice @public */ + public showInfo(): void { + this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = { + id: this.pduInstanceID, + page: 'pdu-instances', + titleName: 'INSTANCEDETAILS' + }; + } +} diff --git a/src/app/utilities/project-link/ProjectLinkComponent.html b/src/app/utilities/project-link/ProjectLinkComponent.html new file mode 100644 index 0000000..6d29f0b --- /dev/null +++ b/src/app/utilities/project-link/ProjectLinkComponent.html @@ -0,0 +1,25 @@ + + + {{value.projectName}}  + + + +{{value.projectName}} \ No newline at end of file diff --git a/src/app/utilities/project-link/ProjectLinkComponent.scss b/src/app/utilities/project-link/ProjectLinkComponent.scss new file mode 100644 index 0000000..152cf8d --- /dev/null +++ b/src/app/utilities/project-link/ProjectLinkComponent.scss @@ -0,0 +1,28 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +@import '../../../assets/scss/mixins/mixin'; +@import '../../../assets/scss/variable'; +.link{ + text-decoration: none !important; + &.activeProject{ + border-radius: 3px; + @include padding-value(2, 2, 2, 2); + text-decoration: none !important; + cursor: default; + } +} \ No newline at end of file diff --git a/src/app/utilities/project-link/ProjectLinkComponent.ts b/src/app/utilities/project-link/ProjectLinkComponent.ts new file mode 100644 index 0000000..35c5b2c --- /dev/null +++ b/src/app/utilities/project-link/ProjectLinkComponent.ts @@ -0,0 +1,81 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Project Link Component. + */ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit } from '@angular/core'; +import { environment } from 'environment'; +import { ProjectData } from 'ProjectModel'; +import { ProjectService } from 'ProjectService'; +import { RestService } from 'RestService'; +import { UserDetail } from 'UserModel'; +/** + * Creating component + * @Component takes ProjectLinkComponent.html as template url + */ +@Component({ + selector: 'app-project-link', + templateUrl: './ProjectLinkComponent.html', + styleUrls: ['./ProjectLinkComponent.scss'], + changeDetection: ChangeDetectionStrategy.OnPush +}) +/** Exporting a class @exports ProjectLinkComponent */ +export class ProjectLinkComponent implements OnInit { + /** Invoke service injectors @public */ + public injector: Injector; + /** Variables holds the selected project @public */ + public selectedProject: string; + /** To get the value from the nspackage via valuePrepareFunction default Property of ng-smarttable @public */ + public value: ProjectData; + /** Variables holds all the projects @public */ + public projectList: {}[] = []; + /** Check the project is present for the user @public */ + public isPresent: boolean = false; + /** Set timeout @private */ + private timeOut: number = 10; + /** Instance of the rest service @private */ + private restService: RestService; + /** Holds all project details @private */ + private projectService: ProjectService; + /** Detect changes for the User Input */ + private cd: ChangeDetectorRef; + constructor(injector: Injector) { + this.injector = injector; + this.projectService = this.injector.get(ProjectService); + this.restService = this.injector.get(RestService); + this.cd = this.injector.get(ChangeDetectorRef); + } + + public ngOnInit(): void { + this.selectedProject = localStorage.getItem('project'); + this.getAdminProjects(); + } + + /** Get the admin projects to be selectable @public */ + public getAdminProjects(): void { + const username: string = localStorage.getItem('username'); + this.restService.getResource(environment.USERS_URL + '/' + username).subscribe((projects: UserDetail) => { + this.projectList = projects.project_role_mappings; + this.isPresent = this.projectList.some((item: ProjectData) => item.project === this.value.project); + setTimeout(() => { + this.cd.detectChanges(); + }, this.timeOut); + }); + } + +} diff --git a/src/app/utilities/projects-action/ProjectsActionComponent.html b/src/app/utilities/projects-action/ProjectsActionComponent.html new file mode 100644 index 0000000..a5d4d4e --- /dev/null +++ b/src/app/utilities/projects-action/ProjectsActionComponent.html @@ -0,0 +1,34 @@ + +
+
+ + +
+
diff --git a/src/app/utilities/projects-action/ProjectsActionComponent.scss b/src/app/utilities/projects-action/ProjectsActionComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/projects-action/ProjectsActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/projects-action/ProjectsActionComponent.ts b/src/app/utilities/projects-action/ProjectsActionComponent.ts new file mode 100644 index 0000000..4ac0051 --- /dev/null +++ b/src/app/utilities/projects-action/ProjectsActionComponent.ts @@ -0,0 +1,78 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Projects Action Component + */ +import { Component, Injector } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { ProjectCreateUpdateComponent } from 'ProjectCreateUpdate'; +import { ProjectData } from 'ProjectModel'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes ProjectsActionComponent.html as template url + */ +@Component({ + selector: 'app-projects-action', + templateUrl: './ProjectsActionComponent.html', + styleUrls: ['./ProjectsActionComponent.scss'] +}) +/** Exporting a class @exports ProjectsActionComponent */ +export class ProjectsActionComponent { + /** To get the value from the nspackage via valuePrepareFunction default Property of ng-smarttable @public */ + public value: ProjectData; + + /** To inject services @public */ + public injector: Injector; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.modalService = this.injector.get(NgbModal); + this.sharedService = this.injector.get(SharedService); + } + + /** Delete project @public */ + public projectDelete(): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** Edit project @public */ + public projectEdit(): void { + const modalRef: NgbModalRef = this.modalService.open(ProjectCreateUpdateComponent, { backdrop: 'static' }); + modalRef.componentInstance.projectType = 'Edit'; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } +} diff --git a/src/app/utilities/roles-action/RolesActionComponent.html b/src/app/utilities/roles-action/RolesActionComponent.html new file mode 100644 index 0000000..5a7453d --- /dev/null +++ b/src/app/utilities/roles-action/RolesActionComponent.html @@ -0,0 +1,34 @@ + +
+
+ + +
+
diff --git a/src/app/utilities/roles-action/RolesActionComponent.scss b/src/app/utilities/roles-action/RolesActionComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/roles-action/RolesActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/roles-action/RolesActionComponent.ts b/src/app/utilities/roles-action/RolesActionComponent.ts new file mode 100644 index 0000000..5b4d85b --- /dev/null +++ b/src/app/utilities/roles-action/RolesActionComponent.ts @@ -0,0 +1,79 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Roles Action Component + */ +import { Component, Injector } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { RoleData } from 'RolesModel'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes RolesActionComponent.html as template url + */ +@Component({ + selector: 'app-roles-action', + templateUrl: './RolesActionComponent.html', + styleUrls: ['./RolesActionComponent.scss'] +}) +/** Exporting a class @exports RolesActionComponent */ +export class RolesActionComponent { + /** To get the role data via valuePrepareFunction default Property of ng-smarttable @public */ + public value: RoleData; + + /** To inject services @public */ + public injector: Injector; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Holds the instance of roter service @private */ + private router: Router; + + constructor(injector: Injector) { + this.injector = injector; + this.modalService = this.injector.get(NgbModal); + this.sharedService = this.injector.get(SharedService); + this.router = this.injector.get(Router); + } + + /** Delete Role click handler @public */ + public deleteRole(): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** Edit Role click handler @public */ + public editRole(): void { + this.router.navigate(['/roles/edit', this.value.identifier]).catch(() => { + // Catch Navigation Error + }); + } + +} diff --git a/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.html b/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.html new file mode 100644 index 0000000..454527e --- /dev/null +++ b/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.html @@ -0,0 +1,27 @@ + +
+ + +
\ No newline at end of file diff --git a/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.scss b/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.ts b/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.ts new file mode 100644 index 0000000..cbf3fc1 --- /dev/null +++ b/src/app/utilities/sdn-controller-action/SDNControllerActionComponent.ts @@ -0,0 +1,83 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file SDN Controller Action Component + */ +import { Component, Injector } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { SDNControllerInfoComponent } from 'SDNControllerInfoComponent'; +import { SDNControllerList } from 'SDNControllerModel'; +import { SharedService } from 'SharedService'; + +/** + * Creating component + * @Component takes SDNControllerActionComponent.html as template url + */ +@Component({ + templateUrl: './SDNControllerActionComponent.html', + styleUrls: ['./SDNControllerActionComponent.scss'] +}) +/** Exporting a class @exports SDNControllerActionComponent */ +export class SDNControllerActionComponent { + /** To get the value from the vnfpackage via valuePrepareFunction default Property of ng-smarttable @public */ + public value: SDNControllerList; + + /** To inject services @public */ + public injector: Injector; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Variables holds SDN ID @private */ + private sdnID: string; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.modalService = this.injector.get(NgbModal); + this.sharedService = this.injector.get(SharedService); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.sdnID = this.value.identifier; + } + + /** Show SDN Controller Information @public */ + public showSDNControllerInfo(): void { + this.modalService.open(SDNControllerInfoComponent, { backdrop: 'static' }).componentInstance.params = { + id: this.sdnID, + page: 'sdn-controller' + }; + } + + /** Delete SDN Controller @public */ + public deleteSDNController(): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } +} diff --git a/src/app/utilities/show-info/ShowInfoComponent.html b/src/app/utilities/show-info/ShowInfoComponent.html new file mode 100644 index 0000000..7fff58d --- /dev/null +++ b/src/app/utilities/show-info/ShowInfoComponent.html @@ -0,0 +1,35 @@ + + + + + \ No newline at end of file diff --git a/src/app/utilities/show-info/ShowInfoComponent.scss b/src/app/utilities/show-info/ShowInfoComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/show-info/ShowInfoComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/show-info/ShowInfoComponent.ts b/src/app/utilities/show-info/ShowInfoComponent.ts new file mode 100644 index 0000000..0fb22e7 --- /dev/null +++ b/src/app/utilities/show-info/ShowInfoComponent.ts @@ -0,0 +1,216 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Info Ns Model + */ +import { HttpClient } from '@angular/common/http'; +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import 'codemirror/addon/dialog/dialog'; +import 'codemirror/addon/display/autorefresh'; +import 'codemirror/addon/display/fullscreen'; +import 'codemirror/addon/edit/closebrackets'; +import 'codemirror/addon/edit/matchbrackets'; +import 'codemirror/addon/fold/brace-fold'; +import 'codemirror/addon/fold/foldcode'; +import 'codemirror/addon/fold/foldgutter'; +import 'codemirror/addon/search/search'; +import 'codemirror/addon/search/searchcursor'; +import 'codemirror/keymap/sublime'; +import 'codemirror/lib/codemirror'; +import 'codemirror/mode/javascript/javascript'; +import 'codemirror/mode/markdown/markdown'; +import 'codemirror/mode/yaml/yaml'; +import { ERRORDATA, URLPARAMS } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { NSDDetails } from 'NSDModel'; +import { RestService } from 'RestService'; +/** Set defaults json as type in information modal @constant */ +const defaults: {} = { + 'text/json': '' +}; +/** + * Creating component + * @Component takes ShowInfoComponent.html as template url + */ +@Component({ + templateUrl: './ShowInfoComponent.html', + styleUrls: ['./ShowInfoComponent.scss'] +}) +/** Exporting a class @exports ShowInfoComponent */ +export class ShowInfoComponent implements OnInit { + /** Invoke service injectors @public */ + public injector: Injector; + + /** dataService to pass the data from one component to another @public */ + public dataService: DataService; + + /** Default variables holds NS data @public */ + public defaults: {} = defaults; + + /** Varaibles to hold http client @public */ + public httpClient: HttpClient; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** variables readOnly holds boolean @public */ + public readOnly: boolean = true; + + /** variables to hold mode changes of editor @public */ + public mode: string = 'text/json'; + + /** To Set Mode @public */ + public modeDefault: string = 'javascript'; + + /** variables to hold options of editor @public */ + public options: {} = { + mode: this.modeDefault, + showCursorWhenSelecting: true, + autofocus: true, + lineNumbers: true, + lineWrapping: true, + foldGutter: true, + gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], + autoCloseBrackets: true, + matchBrackets: true, + theme: 'neat', + keyMap: 'sublime' + }; + + /** Reading the page Name @public */ + public titleName: string; + + /** Check the loading results @public */ + public isLoadingResults: Boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Input contains component objects @private */ + @Input() private params: URLPARAMS; + + /** Instance of the rest service @private */ + private restService: RestService; + + constructor(injector: Injector) { + this.injector = injector; + this.dataService = this.injector.get(DataService); + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.isLoadingResults = true; + this.defaults['text/json'] = ''; + this.titleName = this.params.titleName; + // Checks page and assign URL + if (this.params.page === 'ns-instance') { + this.restService.getResource(environment.NSINSTANCESCONTENT_URL + '/' + this.params.id).subscribe((nsData: NSDDetails[]) => { + this.defaults['text/json'] = JSON.stringify(nsData, null, '\t'); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }, () => { + this.isLoadingResults = false; + }); + } else if (this.params.page === 'ns-history-operation') { + this.restService.getResource(environment.NSHISTORYOPERATIONS_URL + '/' + + this.params.id).subscribe((nsHistoryOpn: {}[]) => { + this.defaults['text/json'] = JSON.stringify(nsHistoryOpn, null, '\t'); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }, () => { + this.isLoadingResults = false; + }); + } else if (this.params.page === 'vnf-instance') { + this.restService.getResource(environment.VNFINSTANCES_URL + '/' + this.params.id).subscribe((vnfData: {}[]) => { + this.defaults['text/json'] = JSON.stringify(vnfData, null, '\t'); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }, () => { + this.isLoadingResults = false; + }); + } else if (this.params.page === 'net-slice-package') { + this.restService.getResource(environment.NETWORKSLICETEMPLATECONTENT_URL + '/' + this.params.id).subscribe((netSliceData: {}[]) => { + this.defaults['text/json'] = JSON.stringify(netSliceData, null, '\t'); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }, () => { + this.isLoadingResults = false; + }); + } else if (this.params.page === 'net-slice-instance') { + this.restService.getResource(environment.NETWORKSLICEINSTANCESCONTENT_URL + '/' + this.params.id) + .subscribe((netSliceInstanceData: {}[]) => { + this.defaults['text/json'] = JSON.stringify(netSliceInstanceData, null, '\t'); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }, () => { + this.isLoadingResults = false; + }); + } else if (this.params.page === 'nst-history-operation') { + this.restService.getResource(environment.NSTHISTORYOPERATIONS_URL + '/' + + this.params.id).subscribe((nstHistoryOpn: {}[]) => { + this.defaults['text/json'] = JSON.stringify(nstHistoryOpn, null, '\t'); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }, () => { + this.isLoadingResults = false; + }); + } else if (this.params.page === 'pdu-instances') { + this.restService.getResource(environment.PDUINSTANCE_URL + '/' + + this.params.id).subscribe((pduInstanceOpn: {}[]) => { + this.defaults['text/json'] = JSON.stringify(pduInstanceOpn, null, '\t'); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }, () => { + this.isLoadingResults = false; + }); + } else if (this.params.page === 'k8s-cluster') { + this.restService.getResource(environment.K8SCLUSTER_URL + '/' + + this.params.id).subscribe((k8sclusterOpn: {}[]) => { + this.defaults['text/json'] = JSON.stringify(k8sclusterOpn, null, '\t'); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }, () => { + this.isLoadingResults = false; + }); + } else if (this.params.page === 'k8s-repo') { + this.restService.getResource(environment.K8REPOS_URL + '/' + + this.params.id).subscribe((k8srepoOpn: {}[]) => { + this.defaults['text/json'] = JSON.stringify(k8srepoOpn, null, '\t'); + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'get'); + }, () => { + this.isLoadingResults = false; + }); + } + } +} diff --git a/src/app/utilities/switch-project/SwitchProjectComponent.html b/src/app/utilities/switch-project/SwitchProjectComponent.html new file mode 100644 index 0000000..948591a --- /dev/null +++ b/src/app/utilities/switch-project/SwitchProjectComponent.html @@ -0,0 +1,40 @@ + +
+ + + +
+ \ No newline at end of file diff --git a/src/app/utilities/switch-project/SwitchProjectComponent.scss b/src/app/utilities/switch-project/SwitchProjectComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/switch-project/SwitchProjectComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/switch-project/SwitchProjectComponent.ts b/src/app/utilities/switch-project/SwitchProjectComponent.ts new file mode 100644 index 0000000..1df6a16 --- /dev/null +++ b/src/app/utilities/switch-project/SwitchProjectComponent.ts @@ -0,0 +1,139 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Switch Project Component + */ +import { HttpClient, HttpHeaders } from '@angular/common/http'; +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { APIURLHEADER, ERRORDATA, LOCALSTORAGE, URLPARAMS } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { RestService } from 'RestService'; + +/** + * Creating component + * @Component takes SwitchProjectComponent.html as template url + */ +@Component({ + templateUrl: './SwitchProjectComponent.html', + styleUrls: ['./SwitchProjectComponent.scss'] +}) +/** Exporting a class @exports SwitchProjectComponent */ +export class SwitchProjectComponent implements OnInit { + /** Invoke service injectors @public */ + public injector: Injector; + + /** dataService to pass the data from one component to another @public */ + public dataService: DataService; + + /** Varaibles to hold http client @public */ + public httpClient: HttpClient; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** FormGroup instance added to the form @ html @public */ + public switchProjectForm: FormGroup; + + /** Form submission Add */ + public submitted: boolean = false; + + /** Check the Projects loading results @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Input contains component objects @private */ + @Input() private params: URLPARAMS; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + constructor(injector: Injector) { + this.injector = injector; + this.dataService = this.injector.get(DataService); + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + this.formBuilder = this.injector.get(FormBuilder); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.switchProjectForm.controls; } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.initializeForm(); + } + + /** initialize Forms @public */ + public initializeForm(): void { + this.switchProjectForm = this.formBuilder.group({ + password: ['', [Validators.required]] + }); + } + + /** Switch project @public */ + public switchProject(): void { + this.submitted = true; + if (!this.switchProjectForm.invalid) { + this.isLoadingResults = true; + this.headers = new HttpHeaders({ + 'Content-Type': 'application/json', + Accept: 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + const payLoad: {} = JSON.stringify({ + username: this.params.username, + password: this.switchProjectForm.value.password, + project_id: this.params.projectID + }); + const apiURLHeader: APIURLHEADER = { + url: environment.GENERATETOKEN_URL, + httpOptions: { headers: this.headers } + }; + this.restService.postResource(apiURLHeader, payLoad).subscribe((data: LOCALSTORAGE) => { + if (data) { + localStorage.setItem('id_token', data.id); + localStorage.setItem('project_id', this.params.projectID); + localStorage.setItem('expires', data.expires.toString()); + localStorage.setItem('username', data.username); + localStorage.setItem('project', data.project_name); + localStorage.setItem('token_state', data.id); + this.activeModal.close(); + location.reload(); + this.isLoadingResults = false; + } + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + this.restService.handleError(error, 'post'); + this.activeModal.close(); + }); + } + } +} diff --git a/src/app/utilities/users-action/UsersActionComponent.html b/src/app/utilities/users-action/UsersActionComponent.html new file mode 100644 index 0000000..9ce546e --- /dev/null +++ b/src/app/utilities/users-action/UsersActionComponent.html @@ -0,0 +1,42 @@ + +
+
+ + +
+
\ No newline at end of file diff --git a/src/app/utilities/users-action/UsersActionComponent.scss b/src/app/utilities/users-action/UsersActionComponent.scss new file mode 100644 index 0000000..fdec4ed --- /dev/null +++ b/src/app/utilities/users-action/UsersActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) + */ diff --git a/src/app/utilities/users-action/UsersActionComponent.ts b/src/app/utilities/users-action/UsersActionComponent.ts new file mode 100644 index 0000000..1d8e895 --- /dev/null +++ b/src/app/utilities/users-action/UsersActionComponent.ts @@ -0,0 +1,101 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Users Action Component + */ +import { Component, Injector } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { AddEditUserComponent } from 'AddEditUserComponent'; +import { MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { ProjectRoleComponent } from 'ProjectRoleComponent'; +import { SharedService } from 'SharedService'; +import { UserData } from 'UserModel'; +/** + * Creating component + * @Component takes UsersActionComponent.html as template url + */ +@Component({ + templateUrl: './UsersActionComponent.html', + styleUrls: ['./UsersActionComponent.scss'] +}) +/** Exporting a class @exports UsersActionComponent */ +export class UsersActionComponent { + /** To inject services @public */ + public injector: Injector; + + /** To get the value from the Users action via valuePrepareFunction default Property of ng-smarttable @public */ + public value: UserData; + + /** handle translate @public */ + public translateService: TranslateService; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.modalService = this.injector.get(NgbModal); + this.sharedService = this.injector.get(SharedService); + this.translateService = this.injector.get(TranslateService); + } + + /** Delete User Account @public */ + public deleteUser(): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** Edit User Account @public */ + public editUserModal(editType: string): void { + const modalRef: NgbModalRef = this.modalService.open(AddEditUserComponent, { backdrop: 'static' }); + modalRef.componentInstance.userID = this.value.identifier; + if (editType === 'editPassword') { + modalRef.componentInstance.userTitle = this.translateService.instant('PAGE.USERS.EDITCREDENTIALS'); + } else { + modalRef.componentInstance.userTitle = this.translateService.instant('PAGE.USERS.EDITUSERNAME'); + } + modalRef.componentInstance.userType = editType; + modalRef.componentInstance.userName = this.value.username; + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** Edit User Account @public */ + public projectRolesModal(): void { + const modalRef: NgbModalRef = this.modalService.open(ProjectRoleComponent, { backdrop: 'static' }); + modalRef.componentInstance.userID = this.value.identifier; + modalRef.componentInstance.userTitle = this.translateService.instant('PAGE.USERS.EDITPROJECTROLEMAPPING'); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } +} diff --git a/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.html b/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.html new file mode 100644 index 0000000..7491d59 --- /dev/null +++ b/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.html @@ -0,0 +1,46 @@ + +
+ + {{'PAGE.DASHBOARD.RUNNINGINSTANCES' | translate}} + + + + + + + + +
\ No newline at end of file diff --git a/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.scss b/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.ts b/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.ts new file mode 100644 index 0000000..57f1f64 --- /dev/null +++ b/src/app/utilities/vim-accounts-action/VimAccountsActionComponent.ts @@ -0,0 +1,104 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Vim AccountsAction Component + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { NSInstanceDetails } from 'NSInstanceModel'; +import { SharedService } from 'SharedService'; +import { VIMData } from 'VimAccountModel'; + +/** + * Creating component + * @Component takes VimAccountsActionComponent.html as template url + */ +@Component({ + selector: 'app-vim-accounts-action', + templateUrl: './VimAccountsActionComponent.html', + styleUrls: ['./VimAccountsActionComponent.scss'] +}) +/** Exporting a class @exports VimAccountsActionComponent */ +export class VimAccountsActionComponent implements OnInit { + /** To get the value from the vimAccounts via valuePrepareFunction default Property of ng-smarttable @public */ + public value: VIMData; + + /** To inject services @public */ + public injector: Injector; + + /** To show Instances running @public */ + public showMapIcon: boolean = false; + + /** To show Details Instances running @public */ + public showInstanceDetails: {}[]; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + /** Variables holds NS ID @private */ + private vimID: string; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.modalService = this.injector.get(NgbModal); + this.router = this.injector.get(Router); + this.sharedService = this.injector.get(SharedService); + } + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.getInstancesDetails(); + } + + /** Delete VIM Account @public */ + public deleteVIMAccount(): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, {backdrop: 'static'}); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** On navigate to Info VimAccount @public */ + public vimInfo(): void { + this.vimID = this.value.identifier; + this.router.navigate(['/vim/info', this.vimID]).catch(() => { + // Catch Navigation Error + }); + } + + /** To show the Instances Info for the particular VimAccount @public */ + public getInstancesDetails(): void { + this.showInstanceDetails = []; + this.value.instancesData.filter((item: NSInstanceDetails) => { + if (item.datacenter === this.value.identifier) { + this.showMapIcon = true; + this.showInstanceDetails.push(item); + } + }); + } +} diff --git a/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.html b/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.html new file mode 100644 index 0000000..333beea --- /dev/null +++ b/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.html @@ -0,0 +1,23 @@ + +
+ +
\ No newline at end of file diff --git a/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.scss b/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.scss new file mode 100644 index 0000000..8c2b739 --- /dev/null +++ b/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ diff --git a/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.ts b/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.ts new file mode 100644 index 0000000..d3a95f2 --- /dev/null +++ b/src/app/utilities/vnf-instances-action/VNFInstancesActionComponent.ts @@ -0,0 +1,67 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file VNF-instancesAction Component + */ +import { Component, Injector } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { ShowInfoComponent } from 'ShowInfoComponent'; +import { VNFInstanceData } from 'VNFInstanceModel'; +/** + * Creating component + * @Component takes VNFInstancesActionComponent.html as template url + */ +@Component({ + templateUrl: './VNFInstancesActionComponent.html', + styleUrls: ['./VNFInstancesActionComponent.scss'] +}) +/** Exporting a class @exports VNFInstancesActionComponent */ +export class VNFInstancesActionComponent { + /** To get the value from the vnfpackage via valuePrepareFunction default Property of ng-smarttable @public */ + public value: VNFInstanceData; + + /** Invoke service injectors @public */ + public injector: Injector; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Contains instance ID @private */ + private instanceID: string; + + constructor(injector: Injector) { + this.injector = injector; + this.modalService = this.injector.get(NgbModal); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.instanceID = this.value.identifier; + } + + /** Shows information using modalservice @public */ + public infoVNF(): void { + this.modalService.open(ShowInfoComponent, { backdrop: 'static' }).componentInstance.params = { + id: this.instanceID, + page: 'vnf-instance', + titleName: 'INSTANCEDETAILS' + }; + } +} diff --git a/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.html b/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.html new file mode 100644 index 0000000..df3aa52 --- /dev/null +++ b/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.html @@ -0,0 +1,51 @@ + +
+ + +
+ + +
+
+ \ No newline at end of file diff --git a/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.scss b/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.ts b/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.ts new file mode 100644 index 0000000..f9d1955 --- /dev/null +++ b/src/app/utilities/vnf-packages-action/VNFPackagesActionComponent.ts @@ -0,0 +1,187 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file VNF-packagesAction Component + */ +import { HttpHeaders } from '@angular/common/http'; +import { ChangeDetectorRef, Component, Injector } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { ClonePackageComponent } from 'ClonePackage'; +import { ERRORDATA, GETAPIURLHEADER, MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { environment } from 'environment'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { ShowContentComponent } from 'ShowContent'; +import { VNFData } from 'VNFDModel'; + +/** + * Creating component + * @Component takes VNFPackagesActionComponent.html as template url + */ +@Component({ + templateUrl: './VNFPackagesActionComponent.html', + styleUrls: ['./VNFPackagesActionComponent.scss'] +}) +/** Exporting a class @exports VNFPackagesActionComponent */ +export class VNFPackagesActionComponent { + /** To get the value from the vnfpackage via valuePrepareFunction default Property of ng-smarttable @public */ + public value: VNFData; + + /** To inject services @public */ + public injector: Injector; + + /** Check the loading results for loader status @public */ + public isLoadingDownloadResult: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Variables holds NS ID @private */ + private vnfID: string; + + /** Variables holds NS name @private */ + private vnfName: string; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Detect changes for the User Input */ + private cd: ChangeDetectorRef; + + /** Set timeout @private */ + private timeOut: number = 1000; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.sharedService = this.injector.get(SharedService); + this.modalService = this.injector.get(NgbModal); + this.router = this.injector.get(Router); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.cd = this.injector.get(ChangeDetectorRef); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.headers = new HttpHeaders({ + Accept: 'application/zip, application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + this.vnfID = this.value.identifier; + this.vnfName = this.value.shortName; + } + + /** Delete VNF packages @public */ + public deleteVNFPackage(): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** Set instance for NSD Edit @public */ + public vnfdEdit(): void { + this.router.navigate(['/packages/vnf/edit/', this.vnfID]).then((nav: {}) => { + // Navigated Successfully + }, (error: Error) => { + // Navigation Error Handler + }); + } + + /** list out all the file content of a descriptors @public */ + public showContent(): void { + this.modalService.open(ShowContentComponent, { backdrop: 'static' }).componentInstance.params = { id: this.vnfID, page: 'vnfd' }; + } + + /** Download VNF Package @public */ + public downloadVNFPackage(): void { + this.isLoadingDownloadResult = true; + const httpOptions: GETAPIURLHEADER = { + headers: this.headers, + responseType: 'blob' + }; + this.restService.getResource(environment.VNFPACKAGES_URL + '/' + this.vnfID + '/package_content', httpOptions) + .subscribe((response: Blob) => { + this.isLoadingDownloadResult = false; + this.changeDetactionforDownload(); + const binaryData: Blob[] = []; + binaryData.push(response); + this.sharedService.downloadFiles(this.vnfName, binaryData, response.type); + }, (error: ERRORDATA) => { + this.isLoadingDownloadResult = false; + this.notifierService.notify('error', this.translateService.instant('ERROR')); + this.changeDetactionforDownload(); + if (typeof error.error === 'object') { + error.error.text().then((data: string): void => { + error.error = JSON.parse(data); + this.restService.handleError(error, 'getBlob'); + }); + } + }); + } + + /** Compose VNF Packages @public */ + public composeVNFPackages(): void { + this.router.navigate(['/packages/vnf/compose/', this.vnfID]).catch(); + } + + /** Change the detaction @public */ + public changeDetactionforDownload(): void { + setTimeout(() => { + this.cd.detectChanges(); + }, this.timeOut); + } + + /** Clone NS Packages @public */ + public cloneVNFPackage(): void { + const cloneModal: NgbModalRef = this.modalService.open(ClonePackageComponent, { backdrop: 'static' }); + cloneModal.componentInstance.params = { id: this.vnfID, page: 'vnfd', name: this.vnfName }; + cloneModal.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } +} diff --git a/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.html b/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.html new file mode 100644 index 0000000..902bb42 --- /dev/null +++ b/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.html @@ -0,0 +1,27 @@ + +
+ + +
\ No newline at end of file diff --git a/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.scss b/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.scss new file mode 100644 index 0000000..031e56e --- /dev/null +++ b/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.ts b/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.ts new file mode 100644 index 0000000..0769863 --- /dev/null +++ b/src/app/utilities/wim-accounts-action/WIMAccountsActionComponent.ts @@ -0,0 +1,83 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file WIM AccountsAction Component + */ +import { Component, Injector } from '@angular/core'; +import { Router } from '@angular/router'; +import { NgbActiveModal, NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { MODALCLOSERESPONSEDATA } from 'CommonModel'; +import { DeleteComponent } from 'DeleteComponent'; +import { SharedService } from 'SharedService'; +import { WIMAccountInfoComponent } from 'WIMAccountInfo'; +import { WIMValue } from 'WIMAccountModel'; + +/** + * Creating component + * @Component takes WIMAccountsActionComponent.html as template url + */ +@Component({ + templateUrl: './WIMAccountsActionComponent.html', + styleUrls: ['./WIMAccountsActionComponent.scss'] +}) +/** Exporting a class @exports WIMAccountsActionComponent */ +export class WIMAccountsActionComponent { + /** To get the value from the vnfpackage via valuePrepareFunction default Property of ng-smarttable @public */ + public value: WIMValue; + + /** To inject services @public */ + public injector: Injector; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Variables holds WIM ID @private */ + private wimID: string; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.modalService = this.injector.get(NgbModal); + this.sharedService = this.injector.get(SharedService); + } + + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.wimID = this.value.identifier; + } + + /** Show WIM account information @public */ + public showWIMAccountInfo(): void { + this.modalService.open(WIMAccountInfoComponent, { backdrop: 'static' }).componentInstance.params = { + id: this.wimID, + page: 'wim-accounts' + }; + } + + /** Delete WIM Account @public */ + public deleteWIMAccount(): void { + const modalRef: NgbModalRef = this.modalService.open(DeleteComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } +} diff --git a/src/app/vim-accounts/VimAccountsComponent.html b/src/app/vim-accounts/VimAccountsComponent.html new file mode 100644 index 0000000..3f96ff8 --- /dev/null +++ b/src/app/vim-accounts/VimAccountsComponent.html @@ -0,0 +1,18 @@ + + \ No newline at end of file diff --git a/src/app/vim-accounts/VimAccountsComponent.scss b/src/app/vim-accounts/VimAccountsComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/vim-accounts/VimAccountsComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/vim-accounts/VimAccountsComponent.ts b/src/app/vim-accounts/VimAccountsComponent.ts new file mode 100644 index 0000000..9951cfc --- /dev/null +++ b/src/app/vim-accounts/VimAccountsComponent.ts @@ -0,0 +1,56 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Vim Account Component. + */ +import { Component, Injector } from '@angular/core'; +import { Router, RouterEvent } from '@angular/router'; + +/** + * Creating component + * @Component takes VimAccountsComponent.html as template url + */ +@Component({ + selector: 'app-vim-accounts', + templateUrl: './VimAccountsComponent.html', + styleUrls: ['./VimAccountsComponent.scss'] +}) +/** Exporting a class @exports VimAccountsComponent */ +export class VimAccountsComponent { + /** Invoke service injectors @public */ + public injector: Injector; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + // creates packages component + constructor(injector: Injector) { + this.injector = injector; + this.router = this.injector.get(Router); + this.router.events.subscribe((event: RouterEvent) => { + this.redirectToList(event.url); + }); + } + + /** Return to list NS Package List */ + public redirectToList(getURL: string): void { + if (getURL === '/vim') { + this.router.navigate(['/vim/details']).catch(); + } + } +} diff --git a/src/app/vim-accounts/VimAccountsModule.ts b/src/app/vim-accounts/VimAccountsModule.ts new file mode 100644 index 0000000..3424bc4 --- /dev/null +++ b/src/app/vim-accounts/VimAccountsModule.ts @@ -0,0 +1,92 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Vim Account module. + */ +import { CommonModule } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterModule, Routes } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { TranslateModule } from '@ngx-translate/core'; +import { DataService } from 'DataService'; +import { InfoVimComponent } from 'InfoVim'; +import { LoaderModule } from 'LoaderModule'; +import { NewVimaccountComponent } from 'NewVimaccount'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { PagePerRowModule } from 'PagePerRowModule'; +import { PageReloadModule } from 'PageReloadModule'; +import { VimAccountDetailsComponent } from 'VimAccountDetails'; +import { VimAccountsComponent } from 'VimAccountsComponent'; + +/** To halndle project information */ +const projectInfo: {} = { title: '{project}', url: '/' }; + +/** const values for dashboard Routes */ +const routes: Routes = [ + { + path: '', + component: VimAccountsComponent, + children: [ + { + path: 'details', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'VIMACCOUNTS', url: null }] + }, + component: VimAccountDetailsComponent + }, + { + path: 'info/:id', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'VIMACCOUNTS', url: '/vim/details' }, { title: '{id}', url: null }] + }, + component: InfoVimComponent + }, + { + path: 'new', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'VIMACCOUNTS', url: '/vim/details' }, { title: 'PAGE.VIMDETAILS.NEWVIM', url: null }] + }, + component: NewVimaccountComponent + } + ] + } +]; + +/** + * Creating @NgModule component for Modules + */ +@NgModule({ + imports: [ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), FormsModule, CommonModule, + HttpClientModule, NgSelectModule, Ng2SmartTableModule, TranslateModule, RouterModule.forChild(routes), NgbModule, + PagePerRowModule, LoaderModule, PageReloadModule], + declarations: [VimAccountsComponent, InfoVimComponent, VimAccountDetailsComponent, NewVimaccountComponent], + providers: [DataService], + entryComponents: [InfoVimComponent] +}) +/** Exporting a class @exports VimAccountsModule */ +export class VimAccountsModule { + /** Variables declared to avoid state-less class */ + private vimModule: string; +} diff --git a/src/app/vim-accounts/info-vim/InfoVimComponent.html b/src/app/vim-accounts/info-vim/InfoVimComponent.html new file mode 100644 index 0000000..2eac832 --- /dev/null +++ b/src/app/vim-accounts/info-vim/InfoVimComponent.html @@ -0,0 +1,47 @@ + +
+
{{'PAGE.VIMDETAILS.VIMACCOUNTDETAILS' | translate}}
+
+
+
+
+ + {{details.value}} +
+
+
+
+ +
+
+
+
+ + {{(details.value !== undefined)?details.value : '--'}} +
+
+
+ + \ No newline at end of file diff --git a/src/app/vim-accounts/info-vim/InfoVimComponent.scss b/src/app/vim-accounts/info-vim/InfoVimComponent.scss new file mode 100644 index 0000000..8c2b739 --- /dev/null +++ b/src/app/vim-accounts/info-vim/InfoVimComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ diff --git a/src/app/vim-accounts/info-vim/InfoVimComponent.ts b/src/app/vim-accounts/info-vim/InfoVimComponent.ts new file mode 100644 index 0000000..d40b696 --- /dev/null +++ b/src/app/vim-accounts/info-vim/InfoVimComponent.ts @@ -0,0 +1,445 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Info VIM Page + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { ERRORDATA } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import * as HttpStatus from 'http-status-codes'; +import { RestService } from 'RestService'; +import { isNullOrUndefined } from 'util'; +import { CONFIG, VimAccountDetails, VIMData } from 'VimAccountModel'; + +/** + * Creating component + * @Component InfoVimComponent.html as template url + */ +@Component({ + selector: 'app-info-vim', + templateUrl: './InfoVimComponent.html', + styleUrls: ['./InfoVimComponent.scss'] +}) +/** Exporting a class @exports InfoVimComponent */ +export class InfoVimComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** vimAccountDetails to populate in InfoVIM Page @private */ + public vimAccountDetails: VimAccountDetails; + + /** Information Top Left @public */ + public configParams: {}[] = []; + + /** Showing more details of collapase */ + public isCollapsed: boolean = true; + + /** Contains vim details @public */ + public vimDetails: {}[]; + + /** Check the Projects loading results @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** variables contains paramsID @private */ + private paramsID: string; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Holds the instance of router class @private */ + private router: Router; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** vimId to populate in InfoVIM Page @private */ + private vimId: string; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private activatedRoute: ActivatedRoute; + + /** Utilizes modal service for any modal operations @private */ + private modalService: NgbModal; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.activatedRoute = this.injector.get(ActivatedRoute); + this.modalService = this.injector.get(NgbModal); + this.router = this.injector.get(Router); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + // tslint:disable-next-line:no-backbone-get-set-outside-model + this.paramsID = this.activatedRoute.snapshot.paramMap.get('id'); + this.dataService.currentMessage.subscribe((data: VIMData) => { + this.vimId = data.identifier; + }); + this.generateData(); + } + + /** Routing to VIM Account Details Page @public */ + public onVimAccountBack(): void { + this.router.navigate(['vim/details']).catch(() => { + // Error Cached + }); + } + + /** Generate Data function @public */ + public generateData(): void { + this.isLoadingResults = true; + this.restService.getResource(environment.VIMACCOUNTS_URL + '/' + this.paramsID) + .subscribe((vimAccountsData: VimAccountDetails) => { + this.showDetails(vimAccountsData); + if (vimAccountsData.config !== undefined) { + if (vimAccountsData.vim_type === 'openstack') { + this.showOpenstackConfig(vimAccountsData.config); + } else if (vimAccountsData.vim_type === 'aws') { + this.awsConfig(vimAccountsData.config); + } else if (vimAccountsData.vim_type === 'openvim' || vimAccountsData.vim_type === 'opennebula') { + this.openVIMOpenNebulaConfig(vimAccountsData.config); + } else if (vimAccountsData.vim_type === 'vmware') { + this.vmwareConfig(vimAccountsData.config); + } else if (vimAccountsData.vim_type === 'azure') { + this.azureConfig(vimAccountsData.config); + } + } + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.isLoadingResults = false; + if (error.error.status === HttpStatus.NOT_FOUND || error.error.status === HttpStatus.UNAUTHORIZED) { + this.router.navigateByUrl('404', { skipLocationChange: true }).catch(); + } else { + this.restService.handleError(error, 'get'); + } + }); + } + + /** show general vim detailed information @public */ + public showDetails(vimAccountsData: VimAccountDetails): void { + this.vimDetails = [ + { + title: 'PAGE.VIMDETAILS.NAME', + value: vimAccountsData.name + }, + { + title: 'PAGE.VIMDETAILS.VIMUSERNAME', + value: vimAccountsData.vim_user + }, + { + title: 'PAGE.VIMDETAILS.VIMURL', + value: vimAccountsData.vim_url + }, + { + title: 'PAGE.VIMDETAILS.VIMTYPE', + value: vimAccountsData.vim_type + }, + { + title: 'PAGE.VIMDETAILS.TENANTNAME', + value: vimAccountsData.vim_tenant_name + }, + { + title: 'PAGE.VIMDETAILS.DESCRIPTION', + value: vimAccountsData.description + }, + { + title: 'PAGE.VIMDETAILS.SCHEMATYPE', + value: vimAccountsData.schema_type + }, + { + title: 'PAGE.VIMDETAILS.SCHEMAVERSION', + value: vimAccountsData.schema_version + } + ]; + } + + /** Openstack Config @public */ + public showOpenstackConfig(config: CONFIG): void { + if (!isNullOrUndefined(config)) { + Object.keys(config).forEach((key: string) => { + if (Array.isArray(config[key])) { + config[key] = JSON.stringify(config[key]); + } + }); + } + let location: string = config.location; + if (!isNullOrUndefined(location)) { + const locationArr: string[] = config.location.split(','); + if (Array.isArray(locationArr)) { + location = locationArr[0]; + } + } + + this.configParams = [ + { + title: 'PAGE.VIMDETAILS.SDNCONTROLLER', + value: config.sdn_controller + }, + { + title: 'PAGE.VIMDETAILS.SDNPORTMAPPING', + value: config.sdn_port_mapping + }, + { + title: 'PAGE.VIMDETAILS.VIMNETWORKNAME', + value: config.vim_network_name + }, + { + title: 'PAGE.VIMDETAILS.SECURITYGROUPS', + value: config.security_groups + }, + { + title: 'PAGE.VIMDETAILS.AVAILABILITYZONE', + value: config.availabilityZone + }, + { + title: 'PAGE.VIMDETAILS.REGIONALNAME', + value: config.region_name + }, + { + title: 'PAGE.VIMDETAILS.INSECURE', + value: config.insecure + }, + { + title: 'PAGE.VIMDETAILS.USEEXISTINGFLAVOURS', + value: config.use_existing_flavors + }, + { + title: 'PAGE.VIMDETAILS.USEINTERNALENDPOINT', + value: config.use_internal_endpoint + }, + { + title: 'PAGE.VIMDETAILS.ADDITIONALCONFIG', + value: config.additional_conf + }, + { + title: 'PAGE.VIMDETAILS.APIVERSION', + value: config.APIversion + }, + { + title: 'PAGE.VIMDETAILS.PROJECTDOMAINID', + value: config.project_domain_id + }, + { + title: 'PAGE.VIMDETAILS.PROJECTDOMAINNAME', + value: config.project_domain_name + }, + { + title: 'PAGE.VIMDETAILS.USERDOMAINID', + value: config.user_domain_id + }, + { + title: 'PAGE.VIMDETAILS.USERDOMAINUSER', + value: config.user_domain_name + }, + { + title: 'PAGE.VIMDETAILS.KEYPAIR', + value: config.keypair + }, + { + title: 'PAGE.VIMDETAILS.DATAPLANEPHYSICALNET', + value: config.dataplane_physical_net + }, + { + title: 'PAGE.VIMDETAILS.USEFLOATINGIP', + value: config.use_floating_ip + }, + { + title: 'PAGE.VIMDETAILS.MICROVERSION', + value: config.microversion + }, + { + title: 'PAGE.VIMDETAILS.VIMLOCATION', + value: location + } + ]; + } + + /** AWS Config @public */ + public awsConfig(config: CONFIG): void { + this.configParams = [ + { + title: 'PAGE.VIMDETAILS.SDNCONTROLLER', + value: config.sdn_controller + }, + { + title: 'PAGE.VIMDETAILS.VPCCIDRBLOCK', + value: config.vpc_cidr_block + }, + { + title: 'PAGE.VIMDETAILS.SDNPORTMAPPING', + value: config.sdn_port_mapping + }, + { + title: 'PAGE.VIMDETAILS.SECURITYGROUPS', + value: config.security_groups + }, + { + title: 'PAGE.VIMDETAILS.VIMNETWORKNAME', + value: config.vim_network_name + }, + { + title: 'PAGE.VIMDETAILS.KEYPAIR', + value: config.keypair + }, + { + title: 'PAGE.VIMDETAILS.REGIONALNAME', + value: config.region_name + }, + { + title: 'PAGE.VIMDETAILS.FLAVORIINFO', + value: config.flavor_info + }, + { + title: 'PAGE.VIMDETAILS.ADDITIONALCONFIG', + value: config.additional_conf + } + ]; + } + + /** Open vim and open nebula config @public */ + public openVIMOpenNebulaConfig(config: CONFIG): void { + this.configParams = [ + { + title: 'PAGE.VIMDETAILS.SDNCONTROLLER', + value: config.sdn_controller + }, + { + title: 'PAGE.VIMDETAILS.SDNPORTMAPPING', + value: config.sdn_port_mapping + }, + { + title: 'PAGE.VIMDETAILS.VIMNETWORKNAME', + value: config.vim_network_name + }, + { + title: 'PAGE.VIMDETAILS.ADDITIONALCONFIG', + value: config.additional_conf + } + ]; + } + + /** vmware config @public */ + public vmwareConfig(config: CONFIG): void { + this.configParams = [ + { + title: 'PAGE.VIMDETAILS.SDNCONTROLLER', + value: config.sdn_controller + }, + { + title: 'PAGE.VIMDETAILS.ORGNAME', + value: config.orgname + }, + { + title: 'PAGE.VIMDETAILS.SDNPORTMAPPING', + value: config.sdn_port_mapping + }, + { + title: 'PAGE.VIMDETAILS.VCENTERIP', + value: config.vcenter_ip + }, + { + title: 'PAGE.VIMDETAILS.VIMNETWORKNAME', + value: config.vim_network_name + }, + { + title: 'PAGE.VIMDETAILS.VCENTERPORT', + value: config.vcenter_port + }, + { + title: 'PAGE.VIMDETAILS.ADMINUSERNAME', + value: config.admin_username + }, + { + title: 'PAGE.VIMDETAILS.VCENTERUSER', + value: config.vcenter_user + }, + { + title: 'PAGE.VIMDETAILS.ADMINPASSWORD', + value: config.admin_password + }, + { + title: 'PAGE.VIMDETAILS.VCENTERPASSWORD', + value: config.vcenter_password + }, + { + title: 'PAGE.VIMDETAILS.NSXMANAGER', + value: config.nsx_manager + }, + { + title: 'PAGE.VIMDETAILS.VROPSSITE', + value: config.vrops_site + }, + { + title: 'PAGE.VIMDETAILS.NSXUSER', + value: config.nsx_user + }, + { + title: 'PAGE.VIMDETAILS.VROPSUSER', + value: config.vrops_user + }, + { + title: 'PAGE.VIMDETAILS.NSXPASSWORD', + value: config.nsx_password + }, + { + title: 'PAGE.VIMDETAILS.VROPSPASSWORD', + value: config.vrops_password + }, + { + title: 'PAGE.VIMDETAILS.ADDITIONALCONFIG', + value: config.additional_conf + } + ]; + } + + /** Azure Config @public */ + public azureConfig(config: CONFIG): void { + this.configParams = [ + { + title: 'PAGE.VIMDETAILS.SUBSCRIPTIONID', + value: config.subscription_id + }, + { + title: 'PAGE.VIMDETAILS.REGIONALNAME', + value: config.region_name + }, + { + title: 'PAGE.VIMDETAILS.RESOURCEGROUP', + value: config.resource_group + }, + { + title: 'PAGE.VIMDETAILS.VNETNAME', + value: config.vnet_name + }, + { + title: 'PAGE.VIMDETAILS.FLAVORSPATTERN', + value: config.flavors_pattern + } + ]; + } +} diff --git a/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.html b/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.html new file mode 100644 index 0000000..cc56017 --- /dev/null +++ b/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.html @@ -0,0 +1,661 @@ + +
+
+
{{'PAGE.VIMDETAILS.NEWVIMACCOUNT' | translate}}
+
+
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ + +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
{{'DOMVALIDATIONS.INVALIDURL' | translate}}
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+
+
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ + +
+
+
+
+ +
+
+ + + {{'PAGE.VIM.LOACTIONINFO' | translate}} +
+
+
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+ +
+ \ No newline at end of file diff --git a/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.scss b/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.scss new file mode 100644 index 0000000..d750ccc --- /dev/null +++ b/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.ts b/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.ts new file mode 100644 index 0000000..bc6fd1e --- /dev/null +++ b/src/app/vim-accounts/new-vimaccount/NewVimaccountComponent.ts @@ -0,0 +1,321 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Vim Account Component. + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, TYPESECTION, VIM_TYPES } from 'CommonModel'; +import { environment } from 'environment'; +import * as jsyaml from 'js-yaml'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; +import { FEATURES, VIMLOCATION, VIMLOCATIONDATA } from 'VimAccountModel'; + +/** + * Creating component + * @Component takes NewVimaccountComponent.html as template url + */ +@Component({ + selector: 'app-new-vimaccount', + templateUrl: './NewVimaccountComponent.html', + styleUrls: ['./NewVimaccountComponent.scss'] +}) +/** Exporting a class @exports NewVimaccountComponent */ +export class NewVimaccountComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** FormGroup vim New Account added to the form @ html @public */ + public vimNewAccountForm: FormGroup; + + /** Supported Vim type for the dropdown */ + public vimType: TYPESECTION[]; + + /** Supported Vim type for the dropdown */ + public selectedVimType: string; + + /** Supported true and false value for the dropdown */ + public boolValue: {}[]; + + /** Form submission Add */ + public submitted: boolean = false; + + /** Showing more details of collapase */ + public isCollapsed: boolean = false; + + /** Vim location values @public */ + public getVIMLocation: VIMLOCATIONDATA[] = []; + + /** Check the Projects loading results @public */ + public isLocationLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** set the longitude value of the selected place @public */ + public setLong: number; + + /** set the latitude value of the selected place @public */ + public setLat: number; + + /** Element ref for fileInput @public */ + @ViewChild('fileInput', { static: true }) public fileInput: ElementRef; + + /** Element ref for fileInput @public */ + @ViewChild('fileInputLabel', { static: true }) public fileInputLabel: ElementRef; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Holds the instance of router class @private */ + private router: Router; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Contains configuration key variables @private */ + private configKeys: string[] = []; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.formBuilder = this.injector.get(FormBuilder); + this.router = this.injector.get(Router); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.sharedService = this.injector.get(SharedService); + + /** Initializing Form Action */ + this.vimNewAccountForm = this.formBuilder.group({ + name: [null, Validators.required], + vim_type: [null, Validators.required], + vim_tenant_name: [null, Validators.required], + description: [null], + vim_url: [null, [Validators.required, Validators.pattern(this.sharedService.REGX_URL_PATTERN)]], + schema_type: [''], + vim_user: [null, Validators.required], + vim_password: [null, Validators.required], + vimconfig: this.paramsBuilder() + }); + } + + /** Generate params for config @public */ + public paramsBuilder(): FormGroup { + return this.formBuilder.group({ + use_existing_flavors: [null], + location: [null], + sdn_controller: [null], + APIversion: [null], + sdn_port_mapping: [null], + project_domain_id: [null], + vim_network_name: [null], + project_domain_name: [null], + config_vim_ype: [null], + user_domain_id: [null], + security_groups: [null], + user_domain_name: [null], + availabilityZone: [null], + keypair: [null], + region_name: [null], + dataplane_physical_net: [null], + insecure: [null], + use_floating_ip: [null], + microversion: [null], + use_internal_endpoint: [null], + additional_conf: [null], + orgname: [null], + vcenter_ip: [null], + vcenter_port: [null], + admin_username: [null], + vcenter_user: [null], + admin_password: [null], + vcenter_password: [null], + nsx_manager: [null], + vrops_site: [null], + nsx_user: [null], + vrops_user: [null], + nsx_password: [null], + vrops_password: [null], + vpc_cidr_block: [null], + flavor_info: [null], + subscription_id: [null], + resource_group: [null], + vnet_name: [null], + flavors_pattern: [null] + }); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.vimNewAccountForm.controls; } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.vimType = VIM_TYPES; + this.boolValue = [ + { id: '', name: 'None' }, + { id: true, name: 'True' }, + { id: false, name: 'False' } + ]; + this.headers = new HttpHeaders({ + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + } + + /** On modal submit newVimAccountSubmit will called @public */ + public newVimAccountSubmit(): void { + this.submitted = true; + if (!this.vimNewAccountForm.invalid) { + this.isLocationLoadingResults = true; + this.configKeys.forEach((key: string) => { + this.vimNewAccountForm.controls.vimconfig.get(key).setValue(JSON.parse(this.vimNewAccountForm.controls.vimconfig.get(key).value)); + }); + this.sharedService.cleanForm(this.vimNewAccountForm); + Object.keys(this.vimNewAccountForm.value.vimconfig).forEach((key: string) => { + if (this.vimNewAccountForm.value.vimconfig[key] === undefined || this.vimNewAccountForm.value.vimconfig[key] === null || + this.vimNewAccountForm.value.vimconfig[key] === '') { + delete this.vimNewAccountForm.value.vimconfig[key]; + } + }); + this.vimNewAccountForm.value.config = this.vimNewAccountForm.value.vimconfig; + delete this.vimNewAccountForm.value.vimconfig; + const apiURLHeader: APIURLHEADER = { + url: environment.VIMACCOUNTS_URL, + httpOptions: { headers: this.headers } + }; + this.restService.postResource(apiURLHeader, this.vimNewAccountForm.value) + .subscribe((result: {}) => { + this.notifierService.notify('success', this.translateService.instant('PAGE.VIM.CREATEDSUCCESSFULLY')); + this.isLocationLoadingResults = false; + this.router.navigate(['vim/details']).catch(() => { + // Error Cached; + }); + // Post the New Vim data and reflect in the VIM Details Page. + }, (error: ERRORDATA) => { + this.configKeys.forEach((key: string) => { + this.vimNewAccountForm.controls.vimconfig.get(key) + .setValue(JSON.stringify(this.vimNewAccountForm.controls.vimconfig.get(key).value)); + }); + this.restService.handleError(error, 'post'); + this.isLocationLoadingResults = false; + }); + } + } + + /** Routing to VIM Account Details Page @public */ + public onVimAccountBack(): void { + this.router.navigate(['vim/details']).catch(() => { + // Error Cached + }); + } + + /** Fetching the location with name,latitude,longitude @public */ + public fetchLocationLatLong(value: string): void { + this.isLocationLoadingResults = true; + const newVIMLocation: VIMLOCATIONDATA[] = []; + const locationTrack: string = environment.MAPLATLONGAPI_URL; + const locationAPIURL: string = locationTrack.replace('{value}', value); + this.restService.getResource(locationAPIURL).subscribe((result: VIMLOCATION) => { + result.features.forEach((getFeturesResult: FEATURES) => { + if ('extent' in getFeturesResult.properties) { + getFeturesResult.properties.extent.forEach((extentResult: number, index: number) => { + if (index === 0) { + this.setLong = extentResult; + } + if (index === 1) { + this.setLat = extentResult; + } + }); + } else { + getFeturesResult.geometry.coordinates.forEach((coordinateResult: number, index: number) => { + if (index === 0) { + this.setLong = coordinateResult; + } + if (index === 1) { + this.setLat = coordinateResult; + } + }); + } + newVIMLocation.push({ + label: getFeturesResult.properties.name + ',' + getFeturesResult.properties.state + ', ' + getFeturesResult.properties.country, + value: getFeturesResult.properties.name + ',' + this.setLong + ',' + this.setLat + }); + }); + this.getVIMLocation = newVIMLocation; + this.isLocationLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLocationLoadingResults = false; + }); + } + + /** Drag and drop feature and fetchind the details of files @private */ + public filesDropped(files: FileList): void { + this.configKeys = []; + if (files && files.length === 1) { + this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => { + const getJson: string = jsyaml.load(fileContent, { json: true }); + Object.keys(getJson).forEach((item: string) => { + if (!isNullOrUndefined(this.vimNewAccountForm.controls.vimconfig.get(item))) { + if (typeof getJson[item] === 'object') { + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.vimNewAccountForm.controls.vimconfig.get(item).setValue(JSON.stringify(getJson[item])); + this.configKeys.push(item); + } else { + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.vimNewAccountForm.controls.vimconfig.get(item).setValue(getJson[item]); + } + } + }); + }).catch((err: string): void => { + if (err === 'typeError') { + this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR')); + } else { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + this.fileInputLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE'); + this.fileInput.nativeElement.value = null; + }); + } else if (files && files.length > 1) { + this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION')); + } + this.fileInputLabel.nativeElement.innerText = files[0].name; + this.fileInput.nativeElement.value = null; + } +} diff --git a/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.html b/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.html new file mode 100644 index 0000000..7feabe2 --- /dev/null +++ b/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.html @@ -0,0 +1,67 @@ + +
+
+
{{'VIMACCOUNTS' | translate}}
+
+
+
+ + +
+ + + +
+
+
+
+ +
+ + +
+
+ + +
+
+
+ + \ No newline at end of file diff --git a/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.scss b/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.scss new file mode 100644 index 0000000..aeb086e --- /dev/null +++ b/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.scss @@ -0,0 +1,73 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +@import '../../../assets/scss/mixins/mixin'; +@import '../../../assets/scss/variable'; +.map { + height: 60vh; + width: 100%; + .ol-popup { + @include background(null, $white, null, null, null); + -webkit-filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); + filter: drop-shadow(0 1px 4px rgba(0,0,0,0.2)); + @include border(all, 1, solid, $gray-80); + @include position_value(absolute, $top: null, $right: null, $bottom: 12px, $left: -50px); + @include wh-value(280px, null); + @include font-style(normal); + @include font(null, .765625rem, 400); + @include line-height(1.5); + text-align: start; + text-decoration: none; + letter-spacing: normal; + word-break: normal; + word-spacing: normal; + white-space: normal; + line-break: auto; + word-wrap: break-word; + @include roundedCorners(2px); + &:after, &:before{ + top: 100%; + @include border(all, 1, solid, transparent); + content: " "; + height: 0; + width: 0; + position: absolute; + pointer-events: none; + } + &:after { + border-top-color: $white; + border-width: 10px; + left: 48px; + margin-left: -10px; + } + &:before { + border-top-color: $gray-80; + border-width: 11px; + left: 48px; + margin-left: -11px; + } + } + .ol-popup-closer { + text-decoration: none; + position: absolute; + top: 2px; + right: 8px; + &:after { + content: "✖"; + } + } +} \ No newline at end of file diff --git a/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.ts b/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.ts new file mode 100644 index 0000000..71455f5 --- /dev/null +++ b/src/app/vim-accounts/vim-account-details/VimAccountDetailsComponent.ts @@ -0,0 +1,476 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Vim Account Component. + */ +import { Component, Injector, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { CONFIGCONSTANT, CONSTANTNUMBER, ERRORDATA, VIM_TYPES } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { LocalDataSource } from 'ng2-smart-table'; +import { NSInstanceDetails } from 'NSInstanceModel'; +import Feature from 'ol/Feature'; +import Point from 'ol/geom/Point'; +import { defaults as defaultInteractions } from 'ol/interaction'; +import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'; +import Map from 'ol/Map'; +import Overlay from 'ol/Overlay'; +import { fromLonLat } from 'ol/proj.js'; +import OSM from 'ol/source/OSM'; +import VectorSource from 'ol/source/Vector'; +import { Icon, Style } from 'ol/style'; +import View from 'ol/View'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; +import { VimAccountDetails, VIMData } from 'VimAccountModel'; +import { VimAccountsActionComponent } from 'VimAccountsAction'; +/** + * Creating component + * @Component takes VimAccountDetailsComponent.html as template url + */ +@Component({ + selector: 'app-vim-account-details', + templateUrl: './VimAccountDetailsComponent.html', + styleUrls: ['./VimAccountDetailsComponent.scss'] +}) +/** Exporting a class @exports VimAccountDetailsComponent */ +export class VimAccountDetailsComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + /** handle translate @public */ + public translateService: TranslateService; + /** initially show the map container@public */ + public showMap: boolean; + /** hide and show popup @public */ + public popupShow: boolean = false; + /** Data of smarttable populate through LocalDataSource @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + /** Columns list of the smart table @public */ + public columnLists: object = {}; + /** Settings for smarttable to populate the table with columns @public */ + public settings: object = {}; + /** initially hide the list@private */ + public showList: boolean; + /** to store locations name @public */ + public getLocation: GetLocation[]; + /** Contains content for map popup @public */ + public popupData: string; + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + /** Class for empty and present data @public */ + public checkDataClass: string; + /** Formation of appropriate Data for LocalDatasource @public */ + public vimData: VIMData[]; + /** operational State init data @public */ + public operationalStateFirstStep: string = CONFIGCONSTANT.vimOperationalStateFirstStep; + /** operational State running data @public */ + public operationalStateSecondStep: string = CONFIGCONSTANT.vimOperationalStateStateSecondStep; + /** operational State failed data @public */ + public operationalStateThirdStep: string = CONFIGCONSTANT.vimOperationalStateThirdStep; + /** NS Instances operational State failed data @public */ + public nsinstancesoperationalStateRunning: string = CONFIGCONSTANT.operationalStateSecondStep; + /** Instance of the rest service @private */ + private restService: RestService; + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + /** Holds the instance of router class @private */ + private router: Router; + /** ns INstance Data @private */ + private nsData: NSInstanceDetails[]; + /** map object @private */ + private map: Map; + /** used to bind marker @private */ + private vectorSource: VectorSource; + /** used to bind vectorSource @private */ + private vectorLayer: VectorLayer; + /** marker @private */ + private marker: Feature; + /** latitude @private */ + private lat: number; + /** longitude @private */ + private lng: number; + /** each vector layer of marker is pushed to layers array @private */ + private layers: VectorLayer[] = []; + /** locationData @private */ + private locationData: VimAccountDetails[]; + /** popup array @private */ + private overLay: Overlay[] = []; + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.router = this.injector.get(Router); + this.translateService = this.injector.get(TranslateService); + } + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.osmMapView(); + this.listView(); + this.generateColumns(); + this.generateSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** smart table Header Colums @public */ + public generateColumns(): void { + this.columnLists = { + name: { title: this.translateService.instant('NAME'), width: '15%', sortDirection: 'asc' }, + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '25%' }, + type: { + title: this.translateService.instant('TYPE'), width: '15%', + filter: { + type: 'list', + config: { + selectText: 'Select', + list: VIM_TYPES + } + } + }, + operationalState: { + title: this.translateService.instant('OPERATIONALSTATUS'), width: '15%', type: 'html', + filter: { + type: 'list', + config: { + selectText: 'Select', + list: [ + { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep }, + { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep }, + { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep } + ] + } + }, + valuePrepareFunction: (cell: VIMData, row: VIMData): string => { + if (row.operationalState === this.operationalStateFirstStep) { + return ` + + `; + } else if (row.operationalState === this.operationalStateSecondStep) { + return ` + + `; + } else if (row.operationalState === this.operationalStateThirdStep) { + return ` + + `; + } else { + return `${row.operationalState}`; + } + } + }, + description: { title: this.translateService.instant('DESCRIPTION'), width: '25%' }, + Actions: { + name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom', + valuePrepareFunction: (cell: VIMData, row: VIMData): VIMData => row, + renderComponent: VimAccountsActionComponent + } + }; + } + + /** smart table Data Settings @public */ + public generateSettings(): void { + this.settings = { + edit: { + editButtonContent: '', + confirmSave: true + }, + delete: { + deleteButtonContent: '', + confirmDelete: true + }, + columns: this.columnLists, + actions: { + add: false, + edit: false, + delete: false, + position: 'right' + }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** smart table listing manipulation @public */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** smart table listing manipulation @public */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'vim-account' }); + this.dataService.changeMessage(event.data); + } + + /** on Navigate to Composer Page @public */ + public composeVIM(): void { + this.router.navigate(['vim/new']).catch(() => { + //empty block + }); + } + + /** To show map conatainer @public */ + public mapView(): void { + this.showList = true; + this.showMap = false; + } + /** To show listview @public */ + public listView(): void { + this.showMap = true; + this.showList = false; + } + /** Load the datasource appropriatetype @public */ + public loadDatasource(getdata: VIMData[]): void { + if (getdata.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(getdata).then((data: boolean) => { + //empty block + }).catch(() => { + //empty block + }); + } + + /** Generate generateVIMData object from loop and return for the datasource @public */ + public generateVIMData(vimAccountData: VimAccountDetails): VIMData { + return { + name: vimAccountData.name, + identifier: vimAccountData._id, + type: vimAccountData.vim_type, + operationalState: vimAccountData._admin.operationalState, + description: vimAccountData.description, + instancesData: this.nsData + }; + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } + + /** Fetching the data from server to Load in the smarttable @protected */ + private generateData(): void { + this.isLoadingResults = true; + this.vimData = []; + this.getNSData().then((): void => { + this.restService.getResource(environment.VIMACCOUNTS_URL).subscribe((vimAccountsData: VimAccountDetails[]) => { + this.locationData = vimAccountsData; + vimAccountsData.forEach((vimAccountData: VimAccountDetails) => { + const vimDataObj: VIMData = this.generateVIMData(vimAccountData); + this.vimData.push(vimDataObj); + }); + this.loadDatasource(this.vimData); + this.removeLayersOverLay(); + this.arrayOfLocation(); + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + }).catch((error: ERRORDATA): void => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } + + /** fetching the nsdata @private */ + private async getNSData(): Promise { + return new Promise((resolve: Function, reject: Function): void => { + this.nsData = []; + this.restService.getResource(environment.NSDINSTANCES_URL).subscribe((nsdInstancesData: NSInstanceDetails[]) => { + const nsRunningInstancesData: NSInstanceDetails[] = nsdInstancesData.filter((instancesData: NSInstanceDetails) => + instancesData['operational-status'] === this.nsinstancesoperationalStateRunning); + this.nsData = nsRunningInstancesData; + resolve(true); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + resolve(true); + }); + }); + } + + /** create map view @private */ + private osmMapView(): void { + this.map = new Map({ + target: 'map', + layers: [new TileLayer({ + source: new OSM() + })], + interactions: defaultInteractions({ + mouseWheelZoom: true + }), + view: new View({ + center: fromLonLat([CONSTANTNUMBER.osmapviewlong, CONSTANTNUMBER.osmapviewlat]), + zoom: 3 + }) + }); + } + + /** remove the layers and overlay @private */ + private removeLayersOverLay(): void { + this.layers.forEach((layer: VectorLayer) => { + this.map.removeLayer(layer); + }); + this.overLay.forEach((lay: Overlay) => { + this.map.removeOverlay(lay); + }); + } + + /** filter locations from vimaccounts @private */ + private arrayOfLocation(): void { + this.getLocation = []; + this.locationData.filter((item: VimAccountDetails) => { + if (item.hasOwnProperty('config')) { + if (item.config.hasOwnProperty('location')) { + this.getLocation.push({ name: item.name, location: item.config.location, id: item._id }); + } + } + }); + if (this.getLocation !== []) { + this.getLocation.filter((loc: GetLocation) => { + if (loc.location !== '') { + const getLatLong: string[] = loc.location.split(','); + this.lng = +getLatLong[CONSTANTNUMBER.splitLongitude]; + this.lat = +getLatLong[CONSTANTNUMBER.splitLatitude]; + this.addMarker(getLatLong[0], loc.id, loc.name); + } + }); + } + } + /** add markers on map @private */ + private addMarker(loc: string, id: string, name: string): void { + const container: HTMLElement = document.getElementById('popup'); + const closer: HTMLElement = document.getElementById('popup-closer'); + this.popupShow = true; + const overlay: Overlay = this.addOverlay(container); + this.marker = this.addFeature(loc, id, name); + this.setStyleMarker(); + this.setVectorSource(); + this.setVectorLayer(); + this.map.addLayer(this.vectorLayer); + this.layers.push(this.vectorLayer); + if (this.layers.length === 1) { + this.markerClickEvent(closer, overlay); + } + } + /** Create an overlay to anchor the popup to the map @private */ + private addOverlay(container: HTMLElement): Overlay { + return new Overlay({ + element: container, + autoPan: true, + autoPanAnimation: { + duration: 250 + } + }); + } + /** Return the Feature of creating a marker in the map @private */ + private addFeature(loc: string, id: string, name: string): Feature { + return new Feature({ + geometry: new Point(fromLonLat([this.lng, this.lat])), + location: loc, + Id: id, + vimName: name + }); + } + /** Set the style of the marker @private */ + private setStyleMarker(): void { + this.marker.setStyle(new Style({ + image: new Icon(({ + crossOrigin: 'anonymous', + src: 'assets/images/map-icon.png' + })) + })); + } + /** Set the map vector source @private */ + private setVectorSource(): void { + this.vectorSource = new VectorSource({ + features: [this.marker] + }); + } + /** Set the map vector layer @private */ + private setVectorLayer(): void { + this.vectorLayer = new VectorLayer({ + source: this.vectorSource + }); + } + /** Add a click handler to the map to render the popup. @private */ + private markerClickEvent(closer: HTMLElement, overlay: Overlay): void { + // tslint:disable-next-line: no-any + this.map.on('singleclick', (evt: any) => { + const feature: Feature = this.map.forEachFeatureAtPixel(evt.pixel, + (f: Feature) => { + return f; + }); + if (feature) { + this.setCoordinates(feature, overlay); + } else { + this.map.removeOverlay(overlay); + } + }); + /** Handle close event for overlay */ + closer.onclick = (): boolean => { + overlay.setPosition(undefined); + closer.blur(); + return false; + }; + } + /** Set the coordinates point if the feature is available @private */ + // tslint:disable-next-line: no-any + private setCoordinates(feature: any, overlay: Overlay): void { + this.popupData = ''; + this.popupData += '

' + feature.values_.vimName + '

'; + this.popupData += '
    '; + const instnaceData: NSInstanceDetails[] = this.nsData.filter((item: NSInstanceDetails) => { + if (item.datacenter === feature.values_.Id) { + this.popupData += '
  • ' + + item.name + '
  • '; + return item; + } + }); + if (instnaceData.length === 0) { + this.popupData += '
  • ' + this.translateService.instant('PAGE.DASHBOARD.NOINSTANCES') + '
  • '; + } + this.popupData += '
'; + const coordinates: number[] = feature.getGeometry().getCoordinates(); + overlay.setPosition(coordinates); + this.overLay.push(overlay); + this.map.addOverlay(overlay); + } +} + +/** interface for get location */ +interface GetLocation { + location: string; + id: string; + name?: string; +} diff --git a/src/app/wim-accounts/WIMAccountsComponent.html b/src/app/wim-accounts/WIMAccountsComponent.html new file mode 100644 index 0000000..3f96ff8 --- /dev/null +++ b/src/app/wim-accounts/WIMAccountsComponent.html @@ -0,0 +1,18 @@ + + \ No newline at end of file diff --git a/src/app/wim-accounts/WIMAccountsComponent.scss b/src/app/wim-accounts/WIMAccountsComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/wim-accounts/WIMAccountsComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/wim-accounts/WIMAccountsComponent.ts b/src/app/wim-accounts/WIMAccountsComponent.ts new file mode 100644 index 0000000..5914f30 --- /dev/null +++ b/src/app/wim-accounts/WIMAccountsComponent.ts @@ -0,0 +1,55 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file WIM Account Component. + */ +import { Component, Injector } from '@angular/core'; +import { Router, RouterEvent } from '@angular/router'; + +/** + * Creating component + * @Component takes WIMAccountsComponent.html as template url + */ +@Component({ + templateUrl: './WIMAccountsComponent.html', + styleUrls: ['./WIMAccountsComponent.scss'] +}) +/** Exporting a class @exports WIMAccountsComponent */ +export class WIMAccountsComponent { + /** Invoke service injectors @public */ + public injector: Injector; + + /** Holds teh instance of AuthService class of type AuthService @private */ + private router: Router; + + // creates packages component + constructor(injector: Injector) { + this.injector = injector; + this.router = this.injector.get(Router); + this.router.events.subscribe((event: RouterEvent) => { + this.redirectToList(event.url); + }); + } + + /** Return to list NS Package List */ + public redirectToList(getURL: string): void { + if (getURL === '/wim') { + this.router.navigate(['/wim/details']).catch(); + } + } +} diff --git a/src/app/wim-accounts/WIMAccountsModule.ts b/src/app/wim-accounts/WIMAccountsModule.ts new file mode 100644 index 0000000..088502f --- /dev/null +++ b/src/app/wim-accounts/WIMAccountsModule.ts @@ -0,0 +1,77 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file WIM Account module. + */ +import { CommonModule } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; +import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ReactiveFormsModule } from '@angular/forms'; +import { RouterModule, Routes } from '@angular/router'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { TranslateModule } from '@ngx-translate/core'; +import { DataService } from 'DataService'; +import { LoaderModule } from 'LoaderModule'; +import { NewWIMAccountComponent } from 'NewWIMAccount'; +import { Ng2SmartTableModule } from 'ng2-smart-table'; +import { PagePerRowModule } from 'PagePerRowModule'; +import { PageReloadModule } from 'PageReloadModule'; +import { WIMAccountDetailsComponent } from 'WIMAccountDetails'; +import { WIMAccountInfoComponent } from 'WIMAccountInfo'; +import { WIMAccountsComponent } from 'WIMAccountsComponent'; + +/** To halndle project information */ +const projectInfo: {} = { title: '{project}', url: '/' }; + +/** const values for dashboard Routes */ +const routes: Routes = [ + { + path: '', + component: WIMAccountsComponent, + children: [ + { + path: 'details', + data: { + breadcrumb: [{ title: 'PAGE.DASHBOARD.DASHBOARD', url: '/' }, { title: 'PAGE.DASHBOARD.PROJECTS', url: '/projects' }, + projectInfo, { title: 'WIMACCOUNTS', url: null }] + }, + component: WIMAccountDetailsComponent + } + ] + } +]; + +/** + * Creating @NgModule component for Modules + */ +@NgModule({ + imports: [ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }), FormsModule, CommonModule, + HttpClientModule, NgSelectModule, Ng2SmartTableModule, TranslateModule, RouterModule.forChild(routes), NgbModule, + PagePerRowModule, LoaderModule, PageReloadModule], + declarations: [WIMAccountsComponent, WIMAccountInfoComponent, WIMAccountDetailsComponent, NewWIMAccountComponent], + providers: [DataService], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + entryComponents: [WIMAccountInfoComponent, NewWIMAccountComponent] +}) +/** Exporting a class @exports WIMAccountsModule */ +export class WIMAccountsModule { + /** Variables declared to avoid state-less class */ + private wimModule: string; +} diff --git a/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.html b/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.html new file mode 100644 index 0000000..a97107f --- /dev/null +++ b/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.html @@ -0,0 +1,97 @@ + +
+ + + +
+ \ No newline at end of file diff --git a/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.scss b/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.ts b/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.ts new file mode 100644 index 0000000..7587b58 --- /dev/null +++ b/src/app/wim-accounts/new-wim-account/NewWIMAccountComponent.ts @@ -0,0 +1,189 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file WIM Account Component. + */ +import { HttpHeaders } from '@angular/common/http'; +import { Component, ElementRef, Injector, OnInit, ViewChild } from '@angular/core'; +import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { NotifierService } from 'angular-notifier'; +import { APIURLHEADER, ERRORDATA, MODALCLOSERESPONSEDATA, TYPESECTION, WIM_TYPES } from 'CommonModel'; +import { environment } from 'environment'; +import * as jsyaml from 'js-yaml'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { isNullOrUndefined } from 'util'; + +/** + * Creating component + * @Component takes NewWIMAccountComponent.html as template url + */ +@Component({ + templateUrl: './NewWIMAccountComponent.html', + styleUrls: ['./NewWIMAccountComponent.scss'] +}) +/** Exporting a class @exports NewWIMAccountComponent */ +export class NewWIMAccountComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Setting wim types in array @public */ + public wimType: TYPESECTION[]; + + /** Set WIM Type select to empty @public */ + public wimTypeMod: string = null; + + /** New WIM account form controls using formgroup @public */ + public wimNewAccountForm: FormGroup; + + /** Form submission Add */ + public submitted: boolean = false; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Check the loading results for loader status @public */ + public isLoadingResults: boolean = false; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Element ref for fileInputConfig @public */ + @ViewChild('fileInputConfig', { static: true }) public fileInputConfig: ElementRef; + + /** Element ref for fileInputConfig @public */ + @ViewChild('fileInputConfigLabel', { static: true }) public fileInputConfigLabel: ElementRef; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Controls the header form @private */ + private headers: HttpHeaders; + + /** FormBuilder instance added to the formBuilder @private */ + private formBuilder: FormBuilder; + + /** Notifier service to popup notification @private */ + private notifierService: NotifierService; + + /** Contains tranlsate instance @private */ + private translateService: TranslateService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.formBuilder = this.injector.get(FormBuilder); + this.notifierService = this.injector.get(NotifierService); + this.translateService = this.injector.get(TranslateService); + this.activeModal = this.injector.get(NgbActiveModal); + this.sharedService = this.injector.get(SharedService); + + /** Initializing Form Action */ + this.wimNewAccountForm = this.formBuilder.group({ + name: ['', Validators.required], + wim_type: ['', Validators.required], + wim_url: ['', [Validators.required, Validators.pattern(this.sharedService.REGX_URL_PATTERN)]], + user: ['', Validators.required], + password: ['', Validators.required], + description: [null], + config: [null] + }); + } + + /** convenience getter for easy access to form fields */ + get f(): FormGroup['controls'] { return this.wimNewAccountForm.controls; } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.wimType = WIM_TYPES; + this.headers = new HttpHeaders({ + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0' + }); + } + + /** On modal submit newWimAccountSubmit will called @public */ + public newWimAccountSubmit(): void { + this.submitted = true; + const modalData: MODALCLOSERESPONSEDATA = { + message: 'Done' + }; + this.sharedService.cleanForm(this.wimNewAccountForm); + if (this.wimNewAccountForm.value.description === '') { + this.wimNewAccountForm.value.description = null; + } + if (isNullOrUndefined(this.wimNewAccountForm.value.config) || this.wimNewAccountForm.value.config === '') { + delete this.wimNewAccountForm.value.config; + } else { + const validJSON: boolean = this.sharedService.checkJson(this.wimNewAccountForm.value.config); + if (validJSON) { + this.wimNewAccountForm.value.config = JSON.parse(this.wimNewAccountForm.value.config); + } else { + this.notifierService.notify('error', this.translateService.instant('INVALIDCONFIG')); + return; + } + } + if (!this.wimNewAccountForm.invalid) { + this.isLoadingResults = true; + const apiURLHeader: APIURLHEADER = { + url: environment.WIMACCOUNTS_URL, + httpOptions: { headers: this.headers } + }; + this.restService.postResource(apiURLHeader, this.wimNewAccountForm.value) + .subscribe((result: {}) => { + this.activeModal.close(modalData); + this.isLoadingResults = false; + this.notifierService.notify('success', this.translateService.instant('PAGE.WIMACCOUNTS.CREATEDSUCCESSFULLY')); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'post'); + this.isLoadingResults = false; + }); + } + } + + /** Config file process @private */ + public configFile(files: FileList): void { + if (files && files.length === 1) { + this.sharedService.getFileString(files, 'yaml').then((fileContent: string): void => { + const getConfigJson: string = jsyaml.load(fileContent, { json: true }); + // tslint:disable-next-line: no-backbone-get-set-outside-model + this.wimNewAccountForm.get('config').setValue(JSON.stringify(getConfigJson)); + }).catch((err: string): void => { + if (err === 'typeError') { + this.notifierService.notify('error', this.translateService.instant('YAMLFILETYPEERRROR')); + } else { + this.notifierService.notify('error', this.translateService.instant('ERROR')); + } + this.fileInputConfigLabel.nativeElement.innerText = this.translateService.instant('CHOOSEFILE'); + this.fileInputConfig.nativeElement.value = null; + }); + } else if (files && files.length > 1) { + this.notifierService.notify('error', this.translateService.instant('DROPFILESVALIDATION')); + } + this.fileInputConfigLabel.nativeElement.innerText = files[0].name; + this.fileInputConfig.nativeElement.value = null; + } +} diff --git a/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.html b/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.html new file mode 100644 index 0000000..2c2d839 --- /dev/null +++ b/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.html @@ -0,0 +1,42 @@ + +
+
{{'WIMACCOUNTS' | translate}}
+ + + +
+
+
+ +
+ + +
+
+ + +
+ \ No newline at end of file diff --git a/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.scss b/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.ts b/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.ts new file mode 100644 index 0000000..b1d16f7 --- /dev/null +++ b/src/app/wim-accounts/wim-account-details/WIMAccountDetailsComponent.ts @@ -0,0 +1,246 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file WIM Account Component. + */ +import { Component, Injector, OnDestroy, OnInit } from '@angular/core'; +import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { TranslateService } from '@ngx-translate/core'; +import { CONFIGCONSTANT, ERRORDATA, MODALCLOSERESPONSEDATA, WIM_TYPES } from 'CommonModel'; +import { DataService } from 'DataService'; +import { environment } from 'environment'; +import { NewWIMAccountComponent } from 'NewWIMAccount'; +import { LocalDataSource } from 'ng2-smart-table'; +import { RestService } from 'RestService'; +import { Subscription } from 'rxjs'; +import { SharedService } from 'SharedService'; +import { WIMAccountData, WIMAccountModel } from 'WIMAccountModel'; +import { WIMAccountsActionComponent } from 'WIMAccountsAction'; +/** + * Creating component + * @Component takes WIMAccountDetailsComponent.html as template url + */ +@Component({ + templateUrl: './WIMAccountDetailsComponent.html', + styleUrls: ['./WIMAccountDetailsComponent.scss'] +}) +/** Exporting a class @exports WIMAccountDetailsComponent */ +export class WIMAccountDetailsComponent implements OnInit, OnDestroy { + /** To inject services @public */ + public injector: Injector; + + /** handle translate @public */ + public translateService: TranslateService; + + /** Data of smarttable populate through LocalDataSource @public */ + public dataSource: LocalDataSource = new LocalDataSource(); + + /** Columns list of the smart table @public */ + public columnLists: object = {}; + + /** Settings for smarttable to populate the table with columns @public */ + public settings: object = {}; + + /** Check the loading results @public */ + public isLoadingResults: boolean = true; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Class for empty and present data @public */ + public checkDataClass: string; + + /** operational State init data @public */ + public operationalStateFirstStep: string = CONFIGCONSTANT.wimOperationalStateFirstStep; + + /** operational State running data @public */ + public operationalStateSecondStep: string = CONFIGCONSTANT.wimOperationalStateStateSecondStep; + + /** operational State failed data @public */ + public operationalStateThirdStep: string = CONFIGCONSTANT.wimOperationalStateThirdStep; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** dataService to pass the data from one component to another @private */ + private dataService: DataService; + + /** Formation of appropriate Data for LocalDatasource @private */ + private wimData: {}[] = []; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + /** Instance of the modal service @private */ + private modalService: NgbModal; + + /** Instance of subscriptions @private */ + private generateDataSub: Subscription; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.dataService = this.injector.get(DataService); + this.sharedService = this.injector.get(SharedService); + this.translateService = this.injector.get(TranslateService); + this.modalService = this.injector.get(NgbModal); + } + /** Lifecyle Hooks the trigger before component is instantiate @public */ + public ngOnInit(): void { + this.generateColumns(); + this.generateSettings(); + this.generateData(); + this.generateDataSub = this.sharedService.dataEvent.subscribe(() => { this.generateData(); }); + } + + /** smart table Header Colums @public */ + public generateColumns(): void { + this.columnLists = { + name: { title: this.translateService.instant('NAME'), width: '20%', sortDirection: 'asc' }, + identifier: { title: this.translateService.instant('IDENTIFIER'), width: '20%' }, + type: { + title: this.translateService.instant('TYPE'), width: '15%', + filter: { + type: 'list', + config: { + selectText: 'Select', + list: WIM_TYPES + } + } + }, + operationalState: { + title: this.translateService.instant('OPERATIONALSTATUS'), width: '15%', type: 'html', + filter: { + type: 'list', + config: { + selectText: 'Select', + list: [ + { value: this.operationalStateFirstStep, title: this.operationalStateFirstStep }, + { value: this.operationalStateSecondStep, title: this.operationalStateSecondStep }, + { value: this.operationalStateThirdStep, title: this.operationalStateThirdStep } + ] + } + }, + valuePrepareFunction: (cell: WIMAccountData, row: WIMAccountData): string => { + if (row.operationalState === this.operationalStateFirstStep) { + return ` + + `; + } else if (row.operationalState === this.operationalStateSecondStep) { + return ` + + `; + } else if (row.operationalState === this.operationalStateThirdStep) { + return ` + + `; + } else { + return `${row.operationalState}`; + } + } + }, + description: { title: this.translateService.instant('DESCRIPTION'), width: '25%' }, + Actions: { + name: 'Action', width: '5%', filter: false, sort: false, title: this.translateService.instant('ACTIONS'), type: 'custom', + valuePrepareFunction: (cell: WIMAccountData, row: WIMAccountData): WIMAccountData => row, + renderComponent: WIMAccountsActionComponent + } + }; + } + + /** smart table Data Settings @public */ + public generateSettings(): void { + this.settings = { + edit: { + editButtonContent: '', confirmSave: true + }, + delete: { + deleteButtonContent: '', confirmDelete: true + }, + columns: this.columnLists, + actions: { + add: false, edit: false, delete: false, position: 'right' + }, + attr: this.sharedService.tableClassConfig(), + pager: this.sharedService.paginationPagerConfig(), + noDataMessage: this.translateService.instant('NODATAMSG') + }; + } + + /** smart table listing manipulation @public */ + public onChange(perPageValue: number): void { + this.dataSource.setPaging(1, perPageValue, true); + } + + /** smart table listing manipulation @public */ + public onUserRowSelect(event: MessageEvent): void { + Object.assign(event.data, { page: 'wim-account' }); + this.dataService.changeMessage(event.data); + } + + /** Compose new WIM Accounts @public */ + public composeWIM(): void { + const modalRef: NgbModalRef = this.modalService.open(NewWIMAccountComponent, { backdrop: 'static' }); + modalRef.result.then((result: MODALCLOSERESPONSEDATA) => { + if (result) { + this.sharedService.callData(); + } + }).catch(); + } + + /** Generate generateWIMData object from loop and return for the datasource @public */ + public generateWIMData(wimAccountData: WIMAccountModel): WIMAccountData { + return { + name: wimAccountData.name, + identifier: wimAccountData._id, + type: wimAccountData.wim_type, + operationalState: wimAccountData._admin.operationalState, + description: wimAccountData.description + }; + } + + /** + * Lifecyle hook which get trigger on component destruction + */ + public ngOnDestroy(): void { + this.generateDataSub.unsubscribe(); + } + + /** Fetching the data from server to Load in the smarttable @protected */ + protected generateData(): void { + this.isLoadingResults = true; + this.wimData = []; + this.restService.getResource(environment.WIMACCOUNTS_URL).subscribe((wimAccountsDetails: {}[]) => { + wimAccountsDetails.forEach((wimAccountsData: WIMAccountModel) => { + const wimDataObj: WIMAccountData = this.generateWIMData(wimAccountsData); + this.wimData.push(wimDataObj); + }); + if (this.wimData.length > 0) { + this.checkDataClass = 'dataTables_present'; + } else { + this.checkDataClass = 'dataTables_empty'; + } + this.dataSource.load(this.wimData).then((data: {}) => { + this.isLoadingResults = false; + }).catch(); + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } +} diff --git a/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.html b/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.html new file mode 100644 index 0000000..25126b8 --- /dev/null +++ b/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.html @@ -0,0 +1,84 @@ + + + + + diff --git a/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.scss b/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.scss new file mode 100644 index 0000000..021d205 --- /dev/null +++ b/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.scss @@ -0,0 +1,17 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ \ No newline at end of file diff --git a/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.ts b/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.ts new file mode 100644 index 0000000..1e7be9f --- /dev/null +++ b/src/app/wim-accounts/wim-account-info/WIMAccountInfoComponent.ts @@ -0,0 +1,96 @@ +/* + Copyright 2020 TATA ELXSI + + Licensed under the Apache License, Version 2.0 (the 'License'); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Author: KUMARAN M (kumaran.m@tataelxsi.co.in), RAJESH S (rajesh.s@tataelxsi.co.in), BARATH KUMAR R (barath.r@tataelxsi.co.in) +*/ +/** + * @file Info WIM Page + */ +import { Component, Injector, Input, OnInit } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { CONFIGCONSTANT, ERRORDATA, URLPARAMS } from 'CommonModel'; +import { environment } from 'environment'; +import { RestService } from 'RestService'; +import { SharedService } from 'SharedService'; +import { WIMAccountModel } from 'WIMAccountModel'; + +/** + * Creating component + * @Component takes WIMAccountInfoComponent.html as template url + */ +@Component({ + templateUrl: './WIMAccountInfoComponent.html', + styleUrls: ['./WIMAccountInfoComponent.scss'] +}) +/** Exporting a class @exports WIMAccountInfoComponent */ +export class WIMAccountInfoComponent implements OnInit { + /** To inject services @public */ + public injector: Injector; + + /** Input contains component objects @public */ + @Input() public params: URLPARAMS; + + /** Contains WIM details @public */ + public wimDetails: WIMAccountModel; + + /** Instance for active modal service @public */ + public activeModal: NgbActiveModal; + + /** Check the loading results for loader status @public */ + public isLoadingResults: boolean = true; + + /** operational State init data @public */ + public operationalStateFirstStep: string = CONFIGCONSTANT.wimOperationalStateFirstStep; + + /** operational State running data @public */ + public operationalStateSecondStep: string = CONFIGCONSTANT.wimOperationalStateStateSecondStep; + + /** operational State failed data @public */ + public operationalStateThirdStep: string = CONFIGCONSTANT.wimOperationalStateThirdStep; + + /** Give the message for the loading @public */ + public message: string = 'PLEASEWAIT'; + + /** Instance of the rest service @private */ + private restService: RestService; + + /** Contains all methods related to shared @private */ + private sharedService: SharedService; + + constructor(injector: Injector) { + this.injector = injector; + this.restService = this.injector.get(RestService); + this.activeModal = this.injector.get(NgbActiveModal); + this.sharedService = this.injector.get(SharedService); + } + + /** + * Lifecyle Hooks the trigger before component is instantiate + */ + public ngOnInit(): void { + this.generateData(); + } + + /** Generate Data function @public */ + public generateData(): void { + this.restService.getResource(environment.WIMACCOUNTS_URL + '/' + this.params.id).subscribe((wimDetails: WIMAccountModel) => { + this.wimDetails = wimDetails; + this.isLoadingResults = false; + }, (error: ERRORDATA) => { + this.restService.handleError(error, 'get'); + this.isLoadingResults = false; + }); + } +} diff --git a/src/assets/config/rolePermissions.json b/src/assets/config/rolePermissions.json new file mode 100644 index 0000000..7111144 --- /dev/null +++ b/src/assets/config/rolePermissions.json @@ -0,0 +1,692 @@ +{ + "rolePermissions": [ + { + "title": "Default", + "permissions": [ + { + "operation": "default", + "value": "NA" + } + ] + }, + { + "title": "Admin", + "permissions": [ + { + "operation": "admin", + "value": "NA" + }, + { + "operation": "admin:get", + "value": "NA" + }, + { + "operation": "admin:post", + "value": "NA" + }, + { + "operation": "admin:patch", + "value": "NA" + }, + { + "operation": "admin:put", + "value": "NA" + }, + { + "operation": "admin:delete", + "value": "NA" + } + ] + }, + { + "title": "NS Descriptors", + "permissions": [ + { + "operation": "nsds", + "value": "NA" + }, + { + "operation": "nsds:get", + "value": "NA" + }, + { + "operation": "nsds:post", + "value": "NA" + }, + { + "operation": "nsds:id:get", + "value": "NA" + }, + { + "operation": "nsds:id:put", + "value": "NA" + }, + { + "operation": "nsds:id:delete", + "value": "NA" + }, + { + "operation": "nsds:id:patch", + "value": "NA" + }, + { + "operation": "nsds:content:post", + "value": "NA" + }, + { + "operation": "nsds:id:content:get", + "value": "NA" + }, + { + "operation": "nsds:id:content:put", + "value": "NA" + }, + { + "operation": "nsds:id:nsd:get", + "value": "NA" + }, + { + "operation": "nsds:id:nsd_artifact:get", + "value": "NA" + } + ] + }, + { + "title": "VNF Descriptors", + "permissions": [ + { + "operation": "vnfds", + "value": "NA" + }, + { + "operation": "vnfds:post", + "value": "NA" + }, + { + "operation": "vnfds:content:post", + "value": "NA" + }, + { + "operation": "vnfds:id:get", + "value": "NA" + }, + { + "operation": "vnfds:id:put", + "value": "NA" + }, + { + "operation": "vnfds:id:delete", + "value": "NA" + }, + { + "operation": "vnfds:id:patch", + "value": "NA" + }, + { + "operation": "vnfds:id:content:get", + "value": "NA" + }, + { + "operation": "vnfds:id:content:put", + "value": "NA" + }, + { + "operation": "vnfds:id:vnfd:get", + "value": "NA" + }, + { + "operation": "vnfds:id:vnfd_artifact:get", + "value": "NA" + }, + { + "operation": "vnfds:id:action:post", + "value": "NA" + }, + { + "operation": "vnfds:vnfpkgops:get", + "value": "NA" + }, + { + "operation": "vnfds:vnfpkgops:id:get", + "value": "NA" + } + ] + }, + { + "title": "NS Instances", + "permissions": [ + { + "operation": "ns_instances", + "value": "NA" + }, + { + "operation": "ns_instances:get", + "value": "NA" + }, + { + "operation": "ns_instances:post", + "value": "NA" + }, + { + "operation": "ns_instances:content:post", + "value": "NA" + }, + { + "operation": "ns_instances:id:get", + "value": "NA" + }, + { + "operation": "ns_instances:id:delete", + "value": "NA" + }, + { + "operation": "ns_instances:id:instantiate:post", + "value": "NA" + }, + { + "operation": "ns_instances:id:terminate:post", + "value": "NA" + }, + { + "operation": "ns_instances:id:action:post", + "value": "NA" + }, + { + "operation": "ns_instances:id:scale:post", + "value": "NA" + }, + { + "operation": "ns_instances:opps:get", + "value": "NA" + }, + { + "operation": "ns_instances:opps:id:get", + "value": "NA" + } + ] + }, + { + "title": "VNF Instances", + "permissions": [ + { + "operation": "vnf_instances", + "value": "NA" + }, + { + "operation": "vnf_instances:get", + "value": "NA" + }, + { + "operation": "vnf_instances:id:get", + "value": "NA" + } + ] + }, + { + "title": "VIMs", + "permissions": [ + { + "operation": "vims", + "value": "NA" + }, + { + "operation": "vims:get", + "value": "NA" + }, + { + "operation": "vims:post", + "value": "NA" + }, + { + "operation": "vims:id:get", + "value": "NA" + }, + { + "operation": "vims:id:delete", + "value": "NA" + }, + { + "operation": "vims:id:patch", + "value": "NA" + } + ] + }, + { + "title": "VIMs Accounts", + "permissions": [ + { + "operation": "vim_accounts", + "value": "NA" + }, + { + "operation": "vim_accounts:get", + "value": "NA" + }, + { + "operation": "vim_accounts:post", + "value": "NA" + }, + { + "operation": "vim_accounts:id:get", + "value": "NA" + }, + { + "operation": "vim_accounts:id:delete", + "value": "NA" + }, + { + "operation": "vim_accounts:id:patch", + "value": "NA" + } + ] + }, + { + "title": "SDN Controllers", + "permissions": [ + { + "operation": "sdn_controllers", + "value": "NA" + }, + { + "operation": "sdn_controllers:get", + "value": "NA" + }, + { + "operation": "sdn_controllers:post", + "value": "NA" + }, + { + "operation": "sdn_controllers:id:get", + "value": "NA" + }, + { + "operation": "sdn_controllers:id:delete", + "value": "NA" + }, + { + "operation": "sdn_controllers:id:patch", + "value": "NA" + } + ] + }, + { + "title": "WIMs", + "permissions": [ + { + "operation": "wims", + "value": "NA" + }, + { + "operation": "wims:get", + "value": "NA" + }, + { + "operation": "wims:post", + "value": "NA" + }, + { + "operation": "wims:id:get", + "value": "NA" + }, + { + "operation": "wims:id:delete", + "value": "NA" + }, + { + "operation": "wims:id:patch", + "value": "NA" + } + ] + }, + { + "title": "WIMs Accounts", + "permissions": [ + { + "operation": "wim_accounts", + "value": "NA" + }, + { + "operation": "wim_accounts:get", + "value": "NA" + }, + { + "operation": "wim_accounts:post", + "value": "NA" + }, + { + "operation": "wim_accounts:id:get", + "value": "NA" + }, + { + "operation": "wim_accounts:id:delete", + "value": "NA" + }, + { + "operation": "wim_accounts:id:patch", + "value": "NA" + } + ] + }, + { + "title": "PDUDs", + "permissions": [ + { + "operation": "pduds", + "value": "NA" + }, + { + "operation": "pduds:get", + "value": "NA" + }, + { + "operation": "pduds:post", + "value": "NA" + }, + { + "operation": "pduds:put", + "value": "NA" + }, + { + "operation": "pduds:delete", + "value": "NA" + }, + { + "operation": "pduds:patch", + "value": "NA" + }, + { + "operation": "pduds:id:get", + "value": "NA" + }, + { + "operation": "pduds:id:post", + "value": "NA" + }, + { + "operation": "pduds:id:put", + "value": "NA" + }, + { + "operation": "pduds:id:delete", + "value": "NA" + }, + { + "operation": "pduds:id:patch", + "value": "NA" + } + ] + }, + { + "title": "Network Slice Templates", + "permissions": [ + { + "operation": "slice_templates", + "value": "NA" + }, + { + "operation": "slice_templates:get", + "value": "NA" + }, + { + "operation": "slice_templates:content:post", + "value": "NA" + }, + { + "operation": "slice_templates:id:get", + "value": "NA" + }, + { + "operation": "slice_templates:id:put", + "value": "NA" + }, + { + "operation": "slice_templates:id:delete", + "value": "NA" + }, + { + "operation": "slice_templates:id:patch", + "value": "NA" + }, + { + "operation": "slice_templates:content:get", + "value": "NA" + }, + { + "operation": "slice_templates:content:put", + "value": "NA" + }, + { + "operation": "slice_templates:id:nst:get", + "value": "NA" + }, + { + "operation": "slice_templates:id:nst_artifact:get", + "value": "NA" + } + ] + }, + { + "title": "Network Slice Instances", + "permissions": [ + { + "operation": "slice_instances", + "value": "NA" + }, + { + "operation": "slice_instances:get", + "value": "NA" + }, + { + "operation": "slice_instances:content:post", + "value": "NA" + }, + { + "operation": "slice_instances:id:get", + "value": "NA" + }, + { + "operation": "slice_instances:id:delete", + "value": "NA" + }, + { + "operation": "slice_instances:post", + "value": "NA" + }, + { + "operation": "slice_instances:id:instantiate:post", + "value": "NA" + }, + { + "operation": "slice_instances:id:terminate:post", + "value": "NA" + }, + { + "operation": "slice_instances:id:action:post", + "value": "NA" + }, + { + "operation": "slice_instances:opps:get", + "value": "NA" + }, + { + "operation": "slice_instances:opps:id:get", + "value": "NA" + } + ] + }, + { + "title": "K8 Clusters", + "permissions": [ + { + "operation": "k8sclusters", + "value": "NA" + }, + { + "operation": "k8sclusters:get", + "value": "NA" + }, + { + "operation": "k8sclusters:post", + "value": "NA" + }, + { + "operation": "k8sclusters:id:get", + "value": "NA" + }, + { + "operation": "k8sclusters:id:delete", + "value": "NA" + }, + { + "operation": "k8sclusters:id:patch", + "value": "NA" + } + ] + }, + { + "title": "K8 Repos", + "permissions": [ + { + "operation": "k8srepos", + "value": "NA" + }, + { + "operation": "k8srepos:get", + "value": "NA" + }, + { + "operation": "k8srepos:post", + "value": "NA" + }, + { + "operation": "k8srepos:id:get", + "value": "NA" + }, + { + "operation": "k8srepos:id:delete", + "value": "NA" + } + ] + }, + { + "title": "Users", + "permissions": [ + { + "operation": "users", + "value": "NA" + }, + { + "operation": "users:get", + "value": "NA" + }, + { + "operation": "users:post", + "value": "NA" + }, + { + "operation": "users:id:get", + "value": "NA" + }, + { + "operation": "users:id:delete", + "value": "NA" + }, + { + "operation": "users:id:patch", + "value": "NA" + }, + { + "operation": "domains:get", + "value": "NA" + } + ] + }, + { + "title": "Projects", + "permissions": [ + { + "operation": "projects", + "value": "NA" + }, + { + "operation": "projects:get", + "value": "NA" + }, + { + "operation": "projects:post", + "value": "NA" + }, + { + "operation": "projects:id:get", + "value": "NA" + }, + { + "operation": "projects:id:patch", + "value": "NA" + }, + { + "operation": "projects:id:delete", + "value": "NA" + } + ] + }, + { + "title": "Roles", + "permissions": [ + { + "operation": "roles", + "value": "NA" + }, + { + "operation": "roles:get", + "value": "NA" + }, + { + "operation": "roles:post", + "value": "NA" + }, + { + "operation": "roles:id:get", + "value": "NA" + }, + { + "operation": "roles:id:delete", + "value": "NA" + }, + { + "operation": "roles:id:patch", + "value": "NA" + } + ] + }, + { + "title": "Tokens", + "permissions": [ + { + "operation": "tokens", + "value": "NA" + }, + { + "operation": "tokens:get", + "value": "NA" + }, + { + "operation": "tokens:post", + "value": "NA" + }, + { + "operation": "tokens:delete", + "value": "NA" + }, + { + "operation": "tokens:id:get", + "value": "NA" + }, + { + "operation": "tokens:id:delete", + "value": "NA" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/assets/i18n/de.json b/src/assets/i18n/de.json new file mode 100644 index 0000000..6c80bf8 --- /dev/null +++ b/src/assets/i18n/de.json @@ -0,0 +1,479 @@ +{ + "OSM": "OSM", + "APPVERSION": "App Version", + "OSMVERSION": "OSM Version", + "OSMSOURCEMANO": "Open Source MANO", + "ADMIN": "Admin", + "ENTRIES": "Einträge", + "COMPOSE": "Komponieren a", + "CREATE": "Erstellen", + "SELECT": "Wählen", + "CANCEL": "Stornieren", + "SAVE": "speichern", + "COUNT": "Anzahl", + "IMAGE": "Bild", + "IPPROFILEREF": "IP-Profil Ref", + "ACTION": "Aktion", + "ADD": "Hinzufügen", + "EDIT": "Bearbeiten", + "APPLY": "Sich bewerben", + "FORCE": "Macht", + "DOWNLOAD": "Herunterladen", + "CONTENT": "Inhalt", + "DELETE": "Löschen", + "FORCEDELETE": "Löschen erzwingen", + "RENAME": "Umbenennen", + "INFO": "Info", + "NSPACKAGES": "NS Pakete", + "VNFPACKAGES": "VNF Pakete", + "INSTANCES": "Instanzen", + "INSTANTIATE": "Instanziieren", + "NSINSTANCES": "NS Instanzen", + "VNFINSTANCES": "VNF Instanzen", + "PDUINSTANCES": "PDU Instanzen", + "VIMACCOUNTS": "VIM Konten", + "WIMACCOUNTS": "WIM-Konten", + "SDNCONTROLLER": "SDN Reglerin", + "NETSLICE": "Netslice", + "PROJECT": "Projekt", + "DOMAIN": "Domain", + "PACKAGES": "Pakete", + "MODIFIED": "Geändert", + "NODATAMSG": "Keine Daten in der Tabelle verfügbar", + "SHORTNAME": "Kurzer Name", + "IDENTIFIER": "Kennung", + "DESCRIPTION": "Beschreibung", + "VENDOR": "Verkäuferin", + "VERSION": "Ausführung", + "ACTIONS": "Aktionen", + "NAME": "Name", + "USAGESTATE": "Nutzungsstatus", + "MODIFICATIONDATE": "Änderungsdatum", + "CREATEDDATE": "Erstellungsdatum", + "OPERATIONALSTATUS": "Betriebs Status", + "OPERATIONALSTATE": "Betriebszustand", + "CONFIGSTATUS": "Konfigurations Status", + "DETAILEDSTATUS": "Detaillierter Status", + "NSDNAME": "Nsd name", + "NSTNAME": "Nst name", + "TYPE": "Art", + "VNFD": "VNFD", + "VNF": "VNF", + "MEMBERINDEX": "Mitglieds Index", + "NS": "NS", + "CREATEDAT": "Hergestellt in", + "CREATED": "Erstellt", + "ALL": "Alle", + "ID": "Id", + "OPERATIONSTATE": "Betriebszustand", + "STARTTIME": "Startzeit", + "STATUSENTEREDTIME": "Status Eingegebene Zeit", + "HISTORYOFOPERATIONS": "Geschichte der Operationen", + "UPDATE": "Aktualisieren", + "READONLYMODE": "Nur-Lese-Modus", + "CURRENTLY": "Zur Zeit", + "ON": "Auf", + "OFF": "aus", + "IN": "im", + "FILES": "Dateien", + "NEW": "Neu", + "RECENTLY": "Vor kurzem", + "TOPOLOGY": "Topologie", + "PLEASEWAIT": "Warten Sie mal", + "LOADING": "Wird geladen", + "RESOURCEORCHESTRATOR": "Ressource Orchestrator", + "VIEW": "Aussicht", + "DROP": "Fallen", + "HERE": "Here", + "MAPVIEW": "Kartenansicht", + "LISTVIEW": "Listenansicht", + "OK": "Okay", + "DELETEDSUCCESSFULLY": "Erfolgreich gelöscht", + "SESSIONEXPIRY": "Sitzung abgelaufen, bitte erneut anmelden", + "DELETECONFIRMPOPUPMESSAGE": "Möchten Sie wirklich löschen?", + "DELETELOADERMESSAGE": "Bitte warten Sie, während der Löschvorgang ausgeführt wird", + "VALUE": "Wert", + "PERFORMACTION": "Aktion ausführen", + "EXECUTE": "Execute", + "EXECNSPRIMITIVE": "Exec NS Primitive", + "PRIMITIVETYPE": "Primitiver Typ", + "VNFPRIMITIVE": "VNF Level Primitive", + "NSPRIMITIVE": "NS Level Primitive", + "DESCRIPTOR": "Deskriptor", + "ERROR": "Etwas ist schief gelaufen. Bitte versuche es erneut", + "SHOWGRAPH": "Grafik anzeigen", + "UPDATESHOWGRAPH": "Grafik aktualisieren und anzeigen", + "CREATEPACKAGE": "Neues Paket erstellen", + "GZFILETYPEERRROR": "Laden Sie nur eine tar.gz-Datei hoch und die Größe sollte 15 MB nicht überschreiten", + "YAMLFILETYPEERRROR": "Laden Sie nur YAML-Dateien hoch und die Größe sollte 15 MB nicht überschreiten", + "JSONFILETYPEERRROR": "Laden Sie nur JSON-Dateien hoch und die Größe sollte 15 MB nicht überschreiten", + "PUBFILETYPEERRROR": "Laden Sie nur PUB-Dateien hoch und die Größe sollte 15 MB nicht überschreiten", + "PACKAGE": "Paket", + "URL": "URL", + "DEPLOYED": "Bereitgestellt", + "ROLES": "Rollen", + "INSTANCEDETAILS": "Instanzdetails", + "IPADDRESS": "IP Adresse", + "MGMT": "Mgmt", + "NETNAME": "Netzname", + "USER": "Benutzerin", + "PORT": "Hafen", + "USERNAME": "Nutzername", + "PASSWORD": "Passwort", + "NODATAERROR": "Beim Abrufen der Informationen ist ein Fehler aufgetreten", + "FREEZE": "Bevriezen", + "UNFREEZE": "Auftauen", + "CLONE": "Klon", + "CLONECONFIRMPOPUPMESSAGE": "Möchten Sie wirklich klonen", + "CLONESUCCESSFULLY": "Paket erfolgreich geklont", + "DROPFILES": "Ziehen Sie die Dateien einfach hierher oder klicken Sie hier, um sie hochzuladen", + "DROPFILESVALIDATION": "Bitte wählen Sie eine zu verarbeitende Datei aus", + "METRICS": "Metriken", + "NOOFHOURS": "Anzahl der Stunden", + "MANDATORYCHECK": "Verplichte velden zijn gemarkeerd met een sterretje (*)", + "K8VERSION": "K8-versie", + "ENTER": "Eingeben", + "SWITCHPROJECT": "Projekt wechseln", + "CURRENTPROJECT": "Derzeitiges Projekt", + "SUBMIT": "Submit", + "REFRESH": "Aktualisierung", + "OPEN": "Öffnen", + "UPLOADCONFIG": "Upload Config", + "FILEUPLOADLABEL": "Oder aus Datei laden", + "CONFIG": "Konfig", + "YAMLCONFIG": "Yaml Konfig", + "CHOOSEFILE": "Datei wählen", + "INVALIDCONFIG": "Ungültige Konfiguration", + "PAGE": { + "DASHBOARD": { + "DASHBOARD": "Instrumententafel", + "RECENTUSERLOG": "Letztes Benutzerprotokoll", + "LOGS": "Protokolle", + "FAILEDINSTANCES": "Fehlgeschlagene Instanzen", + "NOINSTANCES": "Keine Instanzen verfügbar", + "UPTIME": "Betriebszeit", + "RUNNINGINSTANCES": "Laufende Instanzen", + "NETSLICETEMPLATE": "NetSlice Vorlage", + "NETSLICEINSTANCE": "NetSlice Instanzen", + "USERS": "Benutzer", + "PROJECTS": "Projekte", + "USERSETTINGS": "Benutzereinstellungen", + "LOGOUT": "Ausloggen" + }, + "LOGIN": { + "USERNAME": "Nutzername", + "PASSWORD": "Passwort", + "SIGNUP": "Anmelden", + "ACCOUNTCREATEMESSAGE": "Sie haben noch keinen Account?", + "LOGIN": "Anmeldung", + "PASSWORDVALIDMESSAGE": "Passwort wird benötigt", + "USERNAMEVALIDMESSAGE": "Ein Benutzername wird benötigt", + "SIGNINMSG": "Melden Sie sich an, um Ihre Sitzung zu starten", + "PASSWORDMINLENGTHVALIDMESSAGE": "Das Passwort muss aus 8 Zeichen bestehen und mindestens ein Groß-, Klein-, Ziffern- und Sonderzeichen enthalten", + "USERNAMEMINLENGTHVALIDMESSAGE": "Der Benutzername muss aus mindestens 5 Zeichen bestehen" + }, + "INSTANCEINSTANTIATE": { + "NEWINSTANCE": "Neue Instanz", + "NSNAME": "Ns Name", + "DESCRIPTION": "Beschrijving", + "NSID": "Nsd Id", + "SSHKEY": "SSH-Schlüssel", + "VIMACCOUNT": "VIM-Konto", + "SSHKEYMSG": "Fügen Sie hier Ihren Schlüssel ein" + }, + "NSMETRIC": { + "INSTANCESMETRIC": "Instanzmetrik", + "METRICERROR": "Daten in Metriken nicht verfügbar" + }, + "USERSETTINGS": { + "LANGUAGE": "Sprache" + }, + "VIM": { + "CREATEDSUCCESSFULLY": "VIM erfolgreich erstellt", + "LOACTIONINFO": "Geben Sie den Standortnamen ein und klicken Sie auf die Eingabetaste, um den Standort aus der Liste auszuwählen" + }, + "VIMDETAILS": { + "NEWVIM": "Nieuwe VIM", + "VIMACCOUNTDETAILS": "VIM-Kontodetails", + "NAME": "Name", + "VIMUSERNAME": "VIM-Benutzername", + "VIMURL": "VIM-URL", + "VIMTYPE": "Art", + "TENANTNAME": "Name des Mieters", + "DESCRIPTION": "Beschreibung", + "SCHEMATYPE": "Schematyp", + "SCHEMAVERSION": "Schema-Version", + "CONFIGPARAMETERS": "KONFIG-PARAMETER", + "SDNCONTROLLER": "SDN-Controller", + "SDNPORTMAPPING": "SDN-Port-Zuordnung", + "VIMNETWORKNAME": "VIM-Netzwerkname", + "SECURITYGROUPS": "Sicherheitsgruppen", + "AVAILABILITYZONE": "Verfügbarkeitszone", + "REGIONALNAME": "Name der Region", + "INSECURE": "Unsicher", + "USEEXISTINGFLAVOURS": "Verwenden Sie vorhandene Aromen", + "USEINTERNALENDPOINT": "Verwenden Sie den internen Endpunkt", + "APIVERSION": "API-Version", + "PROJECTDOMAINID": "Projektdomänen-ID", + "PROJECTDOMAINNAME": "Projektdomänenname", + "USERDOMAINID": "Benutzer-Domain-ID", + "USERDOMAINUSER": "Benutzer-Domainname", + "KEYPAIR": "Schlüsselpaar", + "DATAPLANEPHYSICALNET": "Dataplane physikalisches Netz", + "USEFLOATINGIP": "Verwenden Sie Floating IP", + "DATAPLANENETVLANRANGE": "Dataplane Net VLAN-Bereich", + "MICROVERSION": "Mikroversion", + "BACKTOVIMACCOUNTS": "Zurück zu VimAccounts", + "VIMPASSWORD": "VIM-Passwort", + "ADDITIONALCONFIG": "Zusätzliche Konfiguration", + "ADDITIONALCONFIGPLACEHOLDER": "{'key1':[...],'key2':{},'key3':''}", + "NEWVIMACCOUNT": "Neues VIM-Konto", + "ORGNAME": "Orgname", + "VCENTERIP": "Vcenter ip", + "VCENTERPORT": "Vcenter-Anschluss", + "ADMINUSERNAME": "Admin-Benutzername", + "VCENTERUSER": "Vcenter-Benutzer", + "ADMINPASSWORD": "Administrator-Passwort", + "VCENTERPASSWORD": "Vcenter Passwort", + "NSXMANAGER": "Nsx Manager", + "VROPSSITE": "Vrops Seite", + "NSXUSER": "Nsx Benutzer", + "VROPSUSER": "Vrops Benutzer", + "NSXPASSWORD": "Nsx Passwort", + "VROPSPASSWORD": "Vrops Passwort", + "VPCCIDRBLOCK": "VPC-CIDR-Block", + "FLAVORIINFO": "Geschmacksinfo", + "VIM_TYPE": "VIM-Typ", + "VIMLOCATION": "VIM-Speicherort", + "SUBSCRIPTIONID": "Abonnement-ID", + "RESOURCEGROUP": "Ressourcengruppe", + "VNETNAME": "VNet Name", + "FLAVORSPATTERN": "Geschmacksmuster" + }, + "WIMACCOUNTS": { + "CREATEDSUCCESSFULLY": "WIM erfolgreich erstellt", + "WIMDETAILS": "WIM-Details", + "NEWWIM": "Neuer WIM", + "SCHEMAVERSION": "Schema-Version", + "RO": "RO", + "ROACCOUNT": "RO-Konto", + "USERNAME": "WIM-Benutzername", + "PASSWORD": "WIM-Passwort" + }, + "NSINSTANCE": { + "NEWNSINSTANCE": "Nieuwe NS", + "CREATEDSUCCESSFULLY": "NS-Instanz erfolgreich erstellt" + }, + "VNFINSTANCE": { + "ADDVNFINSTANCE": "VNF-Instanz hinzufügen" + }, + "PDUINSTANCE": { + "NEWPDUINSTANCE": "Nieuwe PDU", + "PDUTYPE": "PDU-Typ", + "PARAMETERS": "PDU-Instanzparameter", + "ADDINSTANCEPARAMS": "Instanzparameter hinzufügen", + "CREATEDSUCCESSFULLY": "PDU-Instanzen erfolgreich erstellt" + }, + "NETSLICEINSTANCE": { + "CREATENETSLICEINSTANCE": "Maak NSI" + }, + "SDNCONTROLLER": { + "NEWSDNCONTROLLER": "Nieuwe SDN-controller", + "REGISTEREDSDNCONTROLLER": "Registrierte SDN Controller", + "RO": "RO", + "DPID": "DPID", + "CREATEDSUCCESSFULLY": "SDN erfolgreich registriert", + "DPIDPLACEHOLDER": "xx:xx:xx:xx:xx:xx:xx:xx", + "DETAILS": "SDN-Controller-Details" + }, + "USERS": { + "CREATEUSER": "Gebruiker aanmaken", + "NEWUSER": "Neuer Benutzer", + "USERNAME": "Nutzername", + "PASSWORD": "Passwort", + "CONFPASSWORD": "Passwort bestätigen", + "EDITUSER": "Benutzer bearbeiten", + "NEWPASSWORD": "Neues Kennwort", + "DEFAULTPROJECT": "Standardprojekt", + "PASSWORDCONFLICT": "Passwort und Passwort bestätigen stimmen nicht überein", + "PASSWORDMATCH": "Passwort-Übereinstimmung", + "CREATEDSUCCESSFULLY": "Benutzer erfolgreich erstellt", + "EDITEDSUCCESSFULLY": "Benutzer erfolgreich bearbeiten", + "EDITCREDENTIALS": "Passwort ändern", + "EDITUSERNAME": "Benutzernamen ändern", + "PROJECTSROLES": "Projekte Rollen", + "EDITPROJECTROLEMAPPING": "Projektrollenzuordnung bearbeiten", + "ADDMAPPINGS": "Mappings hinzufügen", + "EDITPROJECTROLEERROR": "Bitte geben Sie mindestens eine Projektrollenzuordnung an, um fortzufahren" + }, + "TOPOLOGY": { + "SELECTELEMENT": "Element auswählen", + "VL": "VL", + "VNF": "VNF", + "VNFD": "VNFD", + "CP": "CP", + "NSD": "NSD", + "NS": "NS", + "VIRTUALLINK": "Virtueller Link", + "CONNECTIONPOINT": "Verbindungspunkt", + "INTCONNECTIONPOINT": "Int Verbindungspunkt", + "LINK": "Verknüpfung", + "ADDINGCP": "Bitte wählen Sie einen Verbindungspunkt von {{vnfdname}}, um {{vlname}} zu verknüpfen?", + "INFO": "Info", + "HELP": "Hilfe", + "HELPINFO": { + "CREATEEDGE": "Kante erstellen", + "CREATEEDGEFIRSTSETENCE": "Wählen Sie den ersten Scheitelpunkt aus, indem Sie mit darauf klicken", + "CREATEEDGESECONDSETENCE": "auf einem anderen Scheitelpunkt (anders als der ausgewählte).", + "DELETEEDGEVERTEX": "Kante / Scheitelpunkt löschen", + "DELETEEDGEVERTEXSENTENCE": "Doppelklicken Sie auf Kante / Scheitelpunkt.", + "SPREADEDGE": "Kante spreizen", + "SPREADEDGESENTENCE": "Wählen Sie den Scheitelpunkt aus, indem Sie mit darauf klicken", + "EDGEINFO": "Kanteninformationen anzeigen", + "EDGEINFOSENTENCE": "Wählen Sie die Kante durch Klicken aus. Die Informationen werden auf der linken Seite angezeigt." + }, + "VDU": "VDU", + "INTVL": "IntVL", + "INTCP": "IntCP", + "DATAEMPTY": "Bitte ändere etwas" + }, + "PROJECT": { + "NEWPROJECT": "Nieuw project", + "CREATEDSUCCESSFULLY": "Projekt erfolgreich erstellt", + "UPDATEDSUCCESSFULLY": "Projekt erfolgreich aktualisiert" + }, + "NSPACKAGE": { + "ADDNSPACKAGE": "Verfassen Sie eine neue NS", + "CREATEDSUCCESSFULLY": "NS-Paket erfolgreich erstellt", + "NSCOMPOSE": { + "UPDATEDSUCCESSFULLY": "Erfolgreich geupdated", + "CONFIRMCONNECTIONPOINT": "Bitte bestätigen Sie, um den Verbindungspunkt hinzuzufügen", + "CANNOTLINKVNF": "Sie können einen VNF nicht mit einem VNF verknüpfen", + "CANNOTLINKVL": "Sie können eine VL nicht mit einer VL verknüpfen", + "CANNOTLINKVLVNF": "Sie können eine VL nicht mit einer VNF verknüpfen", + "CANNOTLINKVNFCP": "Sie können einen VNF nicht mit einem CP verknüpfen", + "CANNOTLINKVLCP": "Sie können eine VL nicht mit einem CP verknüpfen", + "CANNOTLINKCP": "Sie können einen CP nicht mit einem CP verknüpfen", + "ADDNSD": "Virtueller Link wurde erfolgreich hinzugefügt", + "ADDVNFD": "VNFD wurde erfolgreich hinzugefügt", + "ADDNS": "Der Verbindungslink wurde erfolgreich hinzugefügt", + "DELETENSD": "Der virtuelle Link wurde erfolgreich gelöscht", + "DELETEVNFD": "VNF erfolgreich gelöscht", + "DELETENS": "Der Verbindungspunkt wurde erfolgreich gelöscht", + "DELETELINK": "Der Link wurde erfolgreich gelöscht", + "MGMTNETWORK": "Mgmt Network", + "VIMNETWORKNAME": "Vim Network Name", + "MEMBER-VNF-INDEX": "member-vnf-index", + "VNFD-ID-REF": "vnfd-id-ref", + "VLD-ID": "vld-id", + "VNFD-CP-REF": "vnfd-Verbindungspunkt-ref" + }, + "EDITPACKAGES": { + "UPDATEDSUCCESSFULLY": "Erfolgreich geupdated" + } + }, + "VNFPACKAGE": { + "ADDVNFPACKAGE": "Erstellen Sie eine neue VNF", + "CREATEDSUCCESSFULLY": "VNF-Paket erfolgreich erstellt", + "VNFCOMPOSE": { + "UPDATEDSUCCESSFULLY": "Erfolgreich geupdated", + "INVALIDSELECTION": "Ungültige Auswahl", + "YOUCANNOTDELETELINK": "Sie können den Link nicht löschen", + "CANNOTLINKVDUANDINTCP": "Sie können vdu nicht mit int_cp verknüpfen", + "CANNOTLINKINTCPANDVDU": "Sie können int_cp nicht mit vdu verknüpfen", + "CANNOTLINKCPANDVNFVL": "Sie können cp nicht mit vnf_vl verknüpfen", + "CANNOTLINKVNFVLANDCP": "Sie können vnf_vl nicht mit cp verknüpfen", + "CANNOTLINKINTCPANDCP": "Sie können intcp nicht mit cp verknüpfen", + "CANNOTLINKCPANDINTCP": "Sie können cp nicht mit int_cp verknüpfen", + "CANNOTLINKVDUANDVDU": "Sie können ein vdu nicht mit einem vdu verknüpfen" + } + }, + "NETSLICE": { + "CREATEDSUCCESSFULLY": "Netslice erfolgreich erstellt", + "TEMPLATECREATEDSUCCESSFULLY": "Netslice-Vorlage erfolgreich erstellt", + "UPDATEDSUCCESSFULLY": "Vorlage erfolgreich aktualisiert" + }, + "NETSLICETEMPLATE": { + "NETSLICETEMPLATEDETAILS": "Netzwerk-Slices-Vorlagendetails" + }, + "NSTINSTANCEINSTANTIATE": { + "NEWINSTANCE": "Neue Instanz", + "NSNAME": "Ns Name", + "DESCRIPTION": "Beschrijving", + "NSTID": "Nst Id", + "SSHKEY": "SSH-Schlüssel", + "VIMACCOUNT": "VIM-Konto", + "SSHKEYMSG": "Fügen Sie Ihren Schlüssel hier ein ..." + }, + "NSPRIMITIVE": { + "PRIMITIVE": "Primitive", + "PRIMITIVEPARAMETERS": "Primitive Parameter", + "ADDPRIMITIVEPARAMS": "Primitive Parameter hinzufügen", + "EXECUTEDSUCCESSFULLY": "NS Primitive Configuration ausgeführt" + }, + "ROLES": { + "CREATEROLE": "Rolle erstellen", + "ROLE": "Rolle", + "PERMISSIONS": "Berechtigungen", + "YAMLPERMISSIONS": "YAML Berechtigungen", + "CREATEDSUCCESSFULLY": "Rolle erfolgreich erstellt", + "UPDATEDSUCCESSFULLY": "Rolle erfolgreich aktualisiert", + "ROLEJSONERROR": "Rollenberechtigungen sollten in einer Schlüsselwertweise bereitgestellt werden", + "ROLEKEYERROR": "Der Wert von '{{roleKey}}' in Rollenberechtigungen sollte boolesch sein", + "EDITROLE": "Rolle bearbeiten", + "PREVIEW": "Vorschau", + "TEXTVIEW": "Textvorschau" + }, + "K8S": { + "MENUK8S": "K8s", + "MENUK8SCLUSTER": "K8s Clusters", + "MENUK8SREPO": "K8s Repos", + "REGISTERK8CLUSTER": "Geregistreerde K8s-clusters", + "ADDK8CLUSTER": "K8s-cluster toevoegen", + "K8SCLUSTERDETAILS": "K8s Clusterdetails", + "REGISTERK8REPO": "Geregistreerde K8s-repository", + "ADDK8REPO": "K8s Repository toevoegen", + "K8SREPODETAILS": "K8s Repository Details", + "CREATEDSUCCESSFULLY": "K8s met succes gemaakt", + "NEWK8SCLUSTER": "Nieuw K8s-cluster", + "NAME": "Naam", + "K8SVERSION": "K8s-versie", + "VIMACCOUNT": "Vim-account", + "DESCRIPTION": "Beschrijving", + "NETS": "Nets", + "NETSPLACEHOLDER": "example: {'net1': 'osm-ext'}", + "CREDENTIALS": "Inloggegevens", + "NEWK8SREPO": "Nieuwe K8s Repository", + "TYPE": "Type", + "URL": "URL" + } + }, + "HTTPERROR": { + "401": "Zugriff verweigert", + "400": "Bitte überprüfen Sie die Anfrage und versuchen Sie es erneut", + "404": "Erwarteter Dienst nicht verfügbar. Bitte versuchen Sie es später erneut.", + "500": "Serverfehler, Bitte versuchen Sie es später noch einmal", + "502": "Slechte gateway, probeer het later opnieuw", + "503": "Dienst vorübergehend nicht verfügbar. Bitte versuchen Sie es später erneut.", + "504": "Gateway timeout fehler. Bitte versuchen Sie es später erneut", + "409": "Bitte versuchen Sie es später noch einmal." + }, + "PAGENOTFOUND": { + "OOPS": "Hoppla!", + "NOTFOUND": "404 Nicht gefunden", + "CONTENT": "Die Seite kann nicht gefunden oder nicht autorisiert werden, ist möglicherweise nicht mehr relevant oder hat ihren Namen geändert", + "MEAN": "In der Zwischenzeit können Sie zu zurückkehren", + "HOME": "Zuhause" + }, + "DOMVALIDATIONS": { + "INVALIDURL": "Geben Sie eine gültige URL ein", + "INVALIDIPADDRESS": "Geben Sie eine gültige IP-Adresse ein", + "INVALIDPORTADDRESS": "Geben Sie eine gültige PORT-Adresse ein", + "INVALIDDPID": "Geben Sie eine gültige DPID ein", + "INVALIDJSON": "Geben Sie ein gültiges JSON-Format ein", + "INVALIDYAML": "Geben Sie ein gültiges YAML-Format ein" + }, + "GRAFANA": { + "METRICSERROR": "Keine gültigen Metriken" + } +} \ No newline at end of file diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json new file mode 100644 index 0000000..d22cbae --- /dev/null +++ b/src/assets/i18n/en.json @@ -0,0 +1,479 @@ +{ + "OSM": "OSM", + "APPVERSION": "App Version", + "OSMVERSION": "OSM Version", + "OSMSOURCEMANO": "Open Source MANO", + "ADMIN": "Admin", + "ENTRIES": "Entries", + "COMPOSE": "Compose a", + "CREATE": "Create", + "SELECT": "Select", + "CANCEL": "Cancel", + "SAVE": "Save", + "ACTION": "Action", + "COUNT": "Count", + "IMAGE": "Image", + "IPPROFILEREF": "IP Profile Ref", + "ADD": "Add", + "EDIT": "Edit", + "APPLY": "Apply", + "FORCE": "Force", + "DOWNLOAD": "Download", + "CONTENT": "Content", + "DELETE": "Delete", + "FORCEDELETE": "Force Delete", + "RENAME": "Rename", + "INFO": "Info", + "NSPACKAGES": "NS Packages", + "VNFPACKAGES": "VNF Packages", + "INSTANCES": "Instances", + "INSTANTIATE": "Instantiate", + "NSINSTANCES": "NS Instances", + "VNFINSTANCES": "VNF Instances", + "PDUINSTANCES": "PDU Instances", + "VIMACCOUNTS": "VIM Accounts", + "WIMACCOUNTS": "WIM Accounts", + "SDNCONTROLLER": "SDN Controller", + "NETSLICE": "Netslice", + "PROJECT": "Project", + "DOMAIN": "Domain", + "PACKAGES": "Packages", + "MODIFIED": "Modified", + "NODATAMSG": "No data available in table", + "SHORTNAME": "Short Name", + "IDENTIFIER": "Identifier", + "DESCRIPTION": "Description", + "VENDOR": "Vendor", + "VERSION": "Version", + "ACTIONS": "Actions", + "NAME": "Name", + "USAGESTATE": "UsageState", + "MODIFICATIONDATE": "Modification Date", + "CREATEDDATE": "Creation Date", + "OPERATIONALSTATUS": "Operational Status", + "OPERATIONALSTATE": "Operational State", + "CONFIGSTATUS": "Config Status", + "DETAILEDSTATUS": "Detailed Status", + "NSDNAME": "Nsd name", + "NSTNAME": "Nst name", + "TYPE": "Type", + "VNFD": "VNFD", + "VNF": "VNF", + "MEMBERINDEX": "Member Index", + "NS": "NS", + "CREATEDAT": "Created At", + "CREATED": "Created", + "ALL": "All", + "ID": "Id", + "OPERATIONSTATE": "Operation State", + "STARTTIME": "Start Time", + "STATUSENTEREDTIME": "Status Entered Time", + "HISTORYOFOPERATIONS": "History Of Operations", + "UPDATE": "Update", + "READONLYMODE": "Read only mode", + "CURRENTLY": "Currently", + "ON": "On", + "OFF": "Off", + "IN": "in", + "FILES": "Files", + "NEW": "New", + "RECENTLY": "Recently", + "TOPOLOGY": "Topology", + "PLEASEWAIT": "Please Wait", + "LOADING": "Loading", + "RESOURCEORCHESTRATOR": "Resource Orchestrator", + "VIEW": "View", + "DROP": "Drop", + "HERE": "Here", + "MAPVIEW": "Map View", + "LISTVIEW": "List View", + "OK": "Ok", + "DELETEDSUCCESSFULLY": "{{title}} deleted successfully", + "SESSIONEXPIRY": "Session expired, please login again", + "DELETECONFIRMPOPUPMESSAGE": "Are you sure want to delete", + "DELETELOADERMESSAGE": "Please wait while {{title}} deletion is in progress", + "VALUE": "Value", + "PERFORMACTION": "Perform Action", + "EXECUTE": "Execute", + "EXECNSPRIMITIVE": "Exec NS Primitive", + "PRIMITIVETYPE": "Primitive Type", + "VNFPRIMITIVE": "VNF Level Primitive", + "NSPRIMITIVE": "NS Level Primitive", + "DESCRIPTOR": "Descriptor", + "ERROR": "Something Went wrong please try again", + "SHOWGRAPH": "Show Graph", + "UPDATESHOWGRAPH": "Update and Show Graph", + "CREATEPACKAGE": "Create New Package", + "GZFILETYPEERRROR": "Upload only tar.gz file and size should not exceed 15 MB", + "YAMLFILETYPEERRROR": "Upload only YAML file and size should not exceed 15 MB", + "JSONFILETYPEERRROR": "Upload only JSON file and size should not exceed 15 MB", + "PUBFILETYPEERRROR": "Upload only PUB file and size should not exceed 15 MB", + "PACKAGE": "Package", + "URL": "URL", + "DEPLOYED": "Deployed", + "ROLES": "Roles", + "INSTANCEDETAILS": "Instance Details", + "IPADDRESS": "IP Address", + "MGMT": "Mgmt", + "NETNAME": "Net Name", + "USER": "User", + "PORT": "Port", + "USERNAME": "Username", + "PASSWORD": "Password", + "NODATAERROR": "An error occurred while retrieving the information", + "FREEZE": "Freeze", + "UNFREEZE": "Unfreeze", + "CLONE": "Clone", + "CLONECONFIRMPOPUPMESSAGE": "Are you sure want to clone", + "CLONESUCCESSFULLY": "Package cloned successfully", + "DROPFILES": "Just drag and drop files or click here to upload files", + "DROPFILESVALIDATION": "Please select one file to process", + "METRICS": "Metrics", + "NOOFHOURS": "No Of Hours", + "MANDATORYCHECK": "Mandatory fields are marked with an asterisk (*)", + "K8VERSION": "K8 Version", + "ENTER": "Enter", + "SWITCHPROJECT": "Switch Project", + "CURRENTPROJECT": "Current Project", + "SUBMIT": "Submit", + "REFRESH": "Refresh", + "OPEN": "Open", + "UPLOADCONFIG": "Upload Config", + "FILEUPLOADLABEL": "Or load from file", + "CONFIG": "Config", + "YAMLCONFIG": "Yaml Config", + "CHOOSEFILE": "Choose File", + "INVALIDCONFIG": "Invalid configuration", + "PAGE": { + "DASHBOARD": { + "DASHBOARD": "Dashboard", + "RECENTUSERLOG": "Recent User Log", + "LOGS": "Logs", + "FAILEDINSTANCES": "Failed Instances", + "NOINSTANCES": "No Instances Available", + "UPTIME": "Uptime", + "RUNNINGINSTANCES": "Running Instances", + "NETSLICETEMPLATE": "NetSlice Template", + "NETSLICEINSTANCE": "NetSlice Instances", + "USERS": "Users", + "PROJECTS": "Projects", + "USERSETTINGS": "User Settings", + "LOGOUT": "Logout" + }, + "LOGIN": { + "USERNAME": "Username", + "PASSWORD": "Password", + "SIGNUP": "Sign Up", + "ACCOUNTCREATEMESSAGE": "Don't have an account?", + "LOGIN": "Log In", + "PASSWORDVALIDMESSAGE": "Password is required", + "USERNAMEVALIDMESSAGE": "Username is required", + "SIGNINMSG": "Sign in to start your session", + "PASSWORDMINLENGTHVALIDMESSAGE": "Password must be 8 characters and contains at least one upper case, lower case, numeric & special character", + "USERNAMEMINLENGTHVALIDMESSAGE": "Username must be at least 5 characters" + }, + "INSTANCEINSTANTIATE": { + "NEWINSTANCE": "New Instance", + "NSNAME": "Ns Name", + "DESCRIPTION": "Description", + "NSID": "Nsd Id", + "SSHKEY": "SSH Key", + "VIMACCOUNT": "VIM Account", + "SSHKEYMSG": "Paste your key here" + }, + "NSMETRIC": { + "INSTANCESMETRIC": "Instances Metric", + "METRICERROR": "Data not available in metrics" + }, + "USERSETTINGS": { + "LANGUAGE": "Language" + }, + "VIM": { + "CREATEDSUCCESSFULLY": "VIM Created Successfully", + "LOACTIONINFO": "Type the location name and click enter button to select the location from the list" + }, + "VIMDETAILS": { + "NEWVIM": "New VIM", + "VIMACCOUNTDETAILS": "VIM Account details", + "NAME": "Name", + "VIMUSERNAME": "VIM Username", + "VIMURL": "VIM URL", + "VIMTYPE": "Type", + "TENANTNAME": "Tenant name", + "DESCRIPTION": "Description", + "SCHEMATYPE": "Schema Type", + "SCHEMAVERSION": "Schema Version", + "CONFIGPARAMETERS": "CONFIG PARAMETERS", + "SDNCONTROLLER": "SDN Controller", + "SDNPORTMAPPING": "SDN Port Mapping", + "VIMNETWORKNAME": "VIM Network Name", + "SECURITYGROUPS": "Security Groups", + "AVAILABILITYZONE": "Availability Zone", + "REGIONALNAME": "Region Name", + "INSECURE": "Insecure", + "USEEXISTINGFLAVOURS": "Use existing flavors", + "USEINTERNALENDPOINT": "Use internal endpoint", + "APIVERSION": "API version", + "PROJECTDOMAINID": "Project domain id", + "PROJECTDOMAINNAME": "Project domain name", + "USERDOMAINID": "User domain id", + "USERDOMAINUSER": "User domain name", + "KEYPAIR": "Keypair", + "DATAPLANEPHYSICALNET": "Dataplane physical net", + "USEFLOATINGIP": "Use floating ip", + "DATAPLANENETVLANRANGE": "Dataplane net vlan range", + "MICROVERSION": "Microversion", + "BACKTOVIMACCOUNTS": "Back to VimAccounts", + "VIMPASSWORD": "VIM Password", + "ADDITIONALCONFIG": "Additional configuration", + "ADDITIONALCONFIGPLACEHOLDER": "{'key1':[...],'key2':{},'key3':''}", + "NEWVIMACCOUNT": "New VIM Account", + "ORGNAME": "Orgname", + "VCENTERIP": "Vcenter ip", + "VCENTERPORT": "Vcenter port", + "ADMINUSERNAME": "Admin username", + "VCENTERUSER": "Vcenter user", + "ADMINPASSWORD": "Admin password", + "VCENTERPASSWORD": "Vcenter password", + "NSXMANAGER": "Nsx manager", + "VROPSSITE": "Vrops site", + "NSXUSER": "Nsx user", + "VROPSUSER": "Vrops user", + "NSXPASSWORD": "Nsx password", + "VROPSPASSWORD": "Vrops password", + "VPCCIDRBLOCK": "VPC cidr block", + "FLAVORIINFO": "Flavor info", + "VIM_TYPE": "VIM Type", + "VIMLOCATION": "VIM Location", + "SUBSCRIPTIONID": "Subscription ID", + "RESOURCEGROUP": "Resource Group", + "VNETNAME": "VNet Name", + "FLAVORSPATTERN": "Flavors Pattern" + }, + "WIMACCOUNTS": { + "CREATEDSUCCESSFULLY": "WIM Created Successfully", + "WIMDETAILS": "WIM Details", + "NEWWIM": "New WIM", + "SCHEMAVERSION": "Schema Version", + "RO": "RO", + "ROACCOUNT": "RO Account", + "USERNAME": "WIM Username", + "PASSWORD": "WIM Password" + }, + "NSINSTANCE": { + "NEWNSINSTANCE": "New NS", + "CREATEDSUCCESSFULLY": "NS Instance Created Successfully" + }, + "VNFINSTANCE": { + "ADDVNFINSTANCE": "Add VNF Instance" + }, + "PDUINSTANCE": { + "NEWPDUINSTANCE": "New PDU", + "PDUTYPE": "PDU Type", + "PARAMETERS": "PDU Instances Parameters", + "ADDINSTANCEPARAMS": "Add Params", + "CREATEDSUCCESSFULLY": "PDU Instances Created Successfully" + }, + "NETSLICEINSTANCE": { + "CREATENETSLICEINSTANCE": "Create NSI" + }, + "SDNCONTROLLER": { + "NEWSDNCONTROLLER": "New SDN Controller", + "REGISTEREDSDNCONTROLLER": "Registered SDN Controllers", + "RO": "RO", + "DPID": "DPID", + "CREATEDSUCCESSFULLY": "SDN Registered Successfully", + "DPIDPLACEHOLDER": "xx:xx:xx:xx:xx:xx:xx:xx", + "DETAILS": "SDN Controller Details" + }, + "USERS": { + "CREATEUSER": "Create User", + "NEWUSER": "New User", + "USERNAME": "User Name", + "PASSWORD": "Password", + "CONFPASSWORD": "Confirm Password", + "EDITUSER": "Edit User", + "NEWPASSWORD": "New Password", + "DEFAULTPROJECT": "Default Project", + "PASSWORDCONFLICT": "Password and confirm password are not matched", + "PASSWORDMATCH": "Password Match", + "CREATEDSUCCESSFULLY": "User Created Successfully", + "EDITEDSUCCESSFULLY": "User Edit Successfully", + "EDITCREDENTIALS": "Change Password", + "EDITUSERNAME": "Change Username", + "PROJECTSROLES": "Projects Roles", + "EDITPROJECTROLEMAPPING": "Edit Project Role Mapping", + "ADDMAPPINGS": "Add Mappings", + "EDITPROJECTROLEERROR": "Please provide at least one project role mapping to continue" + }, + "TOPOLOGY": { + "SELECTELEMENT": "Select Element", + "VL": "VL", + "VNF": "VNF", + "VNFD": "VNFD", + "CP": "CP", + "NSD": "NSD", + "NS": "NS", + "VIRTUALLINK": "Virtual Link", + "CONNECTIONPOINT": "Connection Point", + "INTCONNECTIONPOINT": "Int Connection Point", + "LINK": "Link", + "ADDINGCP": "Please select a connection point of {{vnfdname}} to link {{vlname}}?", + "INFO": "Info", + "HELP": "Help", + "HELPINFO": { + "CREATEEDGE": "Create edge", + "CREATEEDGEFIRSTSETENCE": "Select the first vertex by clicking on it using", + "CREATEEDGESECONDSETENCE": "on another vertex (different than the selected one).", + "DELETEEDGEVERTEX": "Delete edge/vertex", + "DELETEEDGEVERTEXSENTENCE": "Double clicking on edge/vertex.", + "SPREADEDGE": "Spread edge", + "SPREADEDGESENTENCE": "Select the vertex by clicking on it using", + "EDGEINFO": "Show edge information", + "EDGEINFOSENTENCE": "Select the edge by clicking. The information will be shown on the left side." + }, + "VDU": "VDU", + "INTVL": "IntVL", + "INTCP": "IntCP", + "DATAEMPTY": "Please change something" + }, + "PROJECT": { + "NEWPROJECT": "New Project", + "CREATEDSUCCESSFULLY": "Project Created Successfully", + "UPDATEDSUCCESSFULLY": "Project Updated Successfully" + }, + "NSPACKAGE": { + "ADDNSPACKAGE": "Compose a new NS", + "CREATEDSUCCESSFULLY": "NS Package Created Successfully", + "NSCOMPOSE": { + "UPDATEDSUCCESSFULLY": "Descriptor Updated Successfully", + "CONFIRMCONNECTIONPOINT": "Please confirm to add connection point", + "CANNOTLINKVNF": "You can't link a vnf with a vnf", + "CANNOTLINKVL": "You can't link a VL with a VL", + "CANNOTLINKVLVNF": "You can't link a VL with a vnf", + "CANNOTLINKVNFCP": "You can't link a VNF with a CP", + "CANNOTLINKVLCP": "You can't link a VL with a CP", + "CANNOTLINKCP": "You can't link a CP with a CP", + "ADDNSD": "Virtual Link is added succesfully", + "ADDVNFD": "VNFD is added succesfully", + "ADDNS": "Connection Link is added succesfully", + "DELETENSD": "Successfully Deleted the Virtual Link", + "DELETEVNFD": "Successfully Deleted the VNF", + "DELETENS": "Successfully Deleted the Connection point", + "DELETELINK": "Successfully Deleted the Link", + "MGMTNETWORK": "Mgmt Network", + "VIMNETWORKNAME": "Vim Network Name", + "MEMBER-VNF-INDEX": "member-vnf-index", + "VNFD-ID-REF": "vnfd-id-ref", + "VLD-ID": "vld-id", + "VNFD-CP-REF": "vnfd-connection-point-ref" + }, + "EDITPACKAGES": { + "UPDATEDSUCCESSFULLY": "Descriptor Updated Successfully" + } + }, + "VNFPACKAGE": { + "ADDVNFPACKAGE": "Compose a new VNF", + "CREATEDSUCCESSFULLY": "VNF Package Created Successfully", + "VNFCOMPOSE": { + "UPDATEDSUCCESSFULLY": "Descriptor Updated Successfully", + "INVALIDSELECTION": "Invalid Selection", + "YOUCANNOTDELETELINK": "You cannot delete link", + "CANNOTLINKVDUANDINTCP": "You cant link vdu with int_cp", + "CANNOTLINKINTCPANDVDU": "You cant link int_cp with vdu", + "CANNOTLINKCPANDVNFVL": "You cant link cp with vnf_vl", + "CANNOTLINKVNFVLANDCP": "You cant link vnf_vl with cp", + "CANNOTLINKINTCPANDCP": "You cant link intcp with cp", + "CANNOTLINKCPANDINTCP": "You cant link cp with int_cp", + "CANNOTLINKVDUANDVDU": "You can't link a vdu with a vdu" + } + }, + "NETSLICE": { + "CREATEDSUCCESSFULLY": "Netslice Created Successfully", + "TEMPLATECREATEDSUCCESSFULLY": "Netslice Template Created Successfully", + "UPDATEDSUCCESSFULLY": "Template Updated Successfully" + }, + "NETSLICETEMPLATE": { + "NETSLICETEMPLATEDETAILS": "Network Slices Template Details" + }, + "NSTINSTANCEINSTANTIATE": { + "NEWINSTANCE": "New Instance", + "NSNAME": "Ns Name", + "DESCRIPTION": "Description", + "NSTID": "Nst Id", + "SSHKEY": "SSH Key", + "VIMACCOUNT": "VIM Account", + "SSHKEYMSG": "Paste your key here ..." + }, + "NSPRIMITIVE": { + "PRIMITIVE": "Primitive", + "PRIMITIVEPARAMETERS": "Primitive Parameters", + "ADDPRIMITIVEPARAMS": "Add Primitive Params", + "EXECUTEDSUCCESSFULLY": "Executed NS Primitive Configuration" + }, + "ROLES": { + "CREATEROLE": "Create Role", + "ROLE": "Role", + "PERMISSIONS": "Permissions", + "YAMLPERMISSIONS": "YAML Permissions", + "CREATEDSUCCESSFULLY": "Role Created Successfully", + "UPDATEDSUCCESSFULLY": "Role Updated Successfully", + "ROLEJSONERROR": "Role permissions should be provided in a key-value fashion", + "ROLEKEYERROR": "Value of '{{roleKey}}' in a role permissions should be boolean", + "EDITROLE": "Edit Role", + "PREVIEW": "Preview", + "TEXTVIEW": "Text View" + }, + "K8S": { + "MENUK8S": "K8s", + "MENUK8SCLUSTER": "K8s Clusters", + "MENUK8SREPO": "K8s Repos", + "REGISTERK8CLUSTER": "Registered K8s clusters", + "ADDK8CLUSTER": "Add K8s Cluster", + "K8SCLUSTERDETAILS": "K8s Cluster Details", + "REGISTERK8REPO": "Registered K8s repository", + "ADDK8REPO": "Add K8s Repository", + "K8SREPODETAILS": "K8s Repository Details", + "CREATEDSUCCESSFULLY": "K8s Created Successfully", + "NEWK8SCLUSTER": "New K8s Cluster", + "NAME": "Name", + "K8SVERSION": "K8s Version", + "VIMACCOUNT": "Vim Account", + "DESCRIPTION": "Description", + "NETS": "Nets", + "NETSPLACEHOLDER": "example: {'net1': 'osm-ext'}", + "CREDENTIALS": "Credentials", + "NEWK8SREPO": "New K8s Repository", + "TYPE": "Type", + "URL": "URL" + } + }, + "HTTPERROR": { + "401": "Access denied, Lack of permissions", + "400": "Please check the request and try again", + "404": "Expected service not avilable, Please try again later", + "500": "Server error, Please try again later", + "502": "Bad Gateway, Please try again later", + "503": "Service temporarily unavailable, Please try again later", + "504": "Gateway timeout error, Please try again later", + "409": "Please try again later" + }, + "PAGENOTFOUND": { + "OOPS": "Oops!", + "NOTFOUND": "404 Not Found", + "CONTENT": "The page cannot be found or unauthorized, it might be no longer relevant or had its name changed", + "MEAN": "Meanwhile, you may return to", + "HOME": "Home" + }, + "DOMVALIDATIONS": { + "INVALIDURL": "Enter a valid URL", + "INVALIDIPADDRESS": "Enter a valid IP Address", + "INVALIDPORTADDRESS": "Enter a valid PORT Address", + "INVALIDDPID": "Enter a valid DPID", + "INVALIDJSON": "Enter a valid JSON Format", + "INVALIDYAML": "Enter a valid JSON Format" + }, + "GRAFANA": { + "METRICSERROR": "Not a valid metrics" + } +} \ No newline at end of file diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json new file mode 100644 index 0000000..0cd37b1 --- /dev/null +++ b/src/assets/i18n/es.json @@ -0,0 +1,479 @@ +{ + "OSM": "OSM", + "APPVERSION": "Versión de la aplicación", + "OSMVERSION": "Versión OSM", + "OSMSOURCEMANO": "Fuente abierta MANO", + "ADMIN": "Admin", + "ENTRIES": "entradas", + "COMPOSE": "Componer un", + "CREATE": "Crear", + "SELECT": "Seleccionar", + "CANCEL": "Cancelar", + "SAVE": "Salvar", + "COUNT": "contar", + "IMAGE": "Imagen", + "IPPROFILEREF": "IP Profile Ref", + "ACTION": "Acción", + "ADD": "Añadir", + "EDIT": "Editar", + "APPLY": "Aplicar", + "FORCE": "Fuerza", + "DOWNLOAD": "Descargar", + "CONTENT": "contenido", + "DELETE": "Borrar", + "FORCEDELETE": "Eliminar forzado", + "RENAME": "Rebautizar", + "INFO": "Info", + "NSPACKAGES": "NS Paquetes", + "VNFPACKAGES": "VNF Paquetes", + "INSTANCES": "Instancias", + "INSTANTIATE": "Instanciar", + "NSINSTANCES": "Instancias de NS", + "VNFINSTANCES": "Instancias VNF", + "PDUINSTANCES": "Instancias PDU", + "VIMACCOUNTS": "Cuentas VIM", + "WIMACCOUNTS": "Cuentas WIM", + "SDNCONTROLLER": "Controlador SDN", + "NETSLICE": "Netslice", + "PROJECT": "Proyecto", + "DOMAIN": "Dominio", + "PACKAGES": "Paquetes", + "MODIFIED": "Modificado", + "NODATAMSG": "No hay datos disponibles en la tabla", + "SHORTNAME": "Nombre corto", + "IDENTIFIER": "Identificador", + "DESCRIPTION": "Descripción", + "VENDOR": "Vendedor", + "VERSION": "Versión", + "ACTIONS": "Comportamiento", + "NAME": "NOMBRE", + "USAGESTATE": "Estado de uso", + "MODIFICATIONDATE": "Fecha de modificación", + "CREATEDDATE": "Fecha de creación", + "OPERATIONALSTATUS": "Estado operativo", + "OPERATIONALSTATE": "Estado operacional", + "CONFIGSTATUS": "Estado de configuración", + "DETAILEDSTATUS": "Estado detallado", + "NSDNAME": "Nombre nsd", + "NSTNAME": "Nst name", + "TYPE": "Tipo", + "VNFD": "VNFD", + "VNF": "VNF", + "MEMBERINDEX": "Índice de miembros", + "NS": "NS", + "CREATEDAT": "Creado en", + "CREATED": "Creado", + "ALL": "Todas", + "ID": "Id", + "OPERATIONSTATE": "Estado de la operación", + "STARTTIME": "Hora de inicio", + "STATUSENTEREDTIME": "Tiempo ingresado estado", + "HISTORYOFOPERATIONS": "Historia de operaciones", + "UPDATE": "Actualizar", + "READONLYMODE": "Modo de solo lectura", + "CURRENTLY": "Actualmente", + "ON": "En", + "OFF": "APAGADA", + "IN": "en", + "FILES": "Archivos", + "NEW": "Nueva", + "RECENTLY": "Recientemente", + "TOPOLOGY": "Topología", + "PLEASEWAIT": "Por favor espera", + "LOADING": "Cargando", + "RESOURCEORCHESTRATOR": "Orquestador de recursos", + "VIEW": "Ver", + "DROP": "soltar", + "HERE": "aquí", + "MAPVIEW": "Vista del mapa", + "LISTVIEW": "Vista de la lista", + "OK": "Okay", + "DELETEDSUCCESSFULLY": "Eliminada Exitosamente", + "SESSIONEXPIRY": "Sesión expirada, por favor ingrese nuevamente", + "DELETECONFIRMPOPUPMESSAGE": "¿Seguro que quieres eliminar?", + "DELETELOADERMESSAGE": "Por favor espere mientras la eliminación está en progreso", + "VALUE": "Valor", + "PERFORMACTION": "Realizar una acción", + "EXECUTE": "Ejecutar", + "EXECNSPRIMITIVE": "Ejecutar NS Primitiva", + "PRIMITIVETYPE": "Tipo primitivo", + "VNFPRIMITIVE": "Nivel VNF Primitivo", + "NSPRIMITIVE": "NS Level Primitive", + "DESCRIPTOR": "Descriptora", + "ERROR": "Algo salió mal. Por favor, vuelva a intentarlo", + "SHOWGRAPH": "Mostrar gráfico", + "UPDATESHOWGRAPH": "Actualizar y mostrar gráfico", + "CREATEPACKAGE": "Crear nuevo paquete", + "GZFILETYPEERRROR": "Cargue solo el archivo tar.gz y el tamaño no debe exceder los 15 MB", + "YAMLFILETYPEERRROR": "Cargue solo el archivo YAML y el tamaño no debe exceder los 15 MB", + "JSONFILETYPEERRROR": "Cargue solo el archivo JSON y el tamaño no debe exceder los 15 MB", + "PUBFILETYPEERRROR": "Cargue solo el archivo PUB y el tamaño no debe exceder los 15 MB", + "PACKAGE": "Paquete", + "URL": "URL", + "DEPLOYED": "Desplegada", + "ROLES": "Roles", + "INSTANCEDETAILS": "Detalles de instancia", + "IPADDRESS": "Dirección IP", + "MGMT": "Mgmt", + "NETNAME": "Nombre neto", + "USER": "Usuaria", + "PORT": "Puerto", + "USERNAME": "Nombre de usuario", + "PASSWORD": "Contraseña", + "NODATAERROR": "Se produjo un error al recuperar la información", + "FREEZE": "Congelar", + "UNFREEZE": "Descongelar", + "CLONE": "Clon", + "CLONECONFIRMPOPUPMESSAGE": "Estás seguro de querer clonar", + "CLONESUCCESSFULLY": "Paquete clonado exitosamente", + "DROPFILES": "Simplemente arrastre y suelte archivos aquí o haga clic aquí para cargar archivos", + "DROPFILESVALIDATION": "Por favor seleccione un archivo para procesar", + "METRICS": "Métrica", + "NOOFHOURS": "No de horas", + "MANDATORYCHECK": "Los campos obligatorios están marcados con un asterisco (*)", + "K8VERSION": "Versión K8", + "ENTER": "Entrar", + "SWITCHPROJECT": "Cambiar proyecto", + "CURRENTPROJECT": "Proyecto actual", + "SUBMIT": "Entrar", + "REFRESH": "Actualizar", + "OPEN": "Abierta", + "UPLOADCONFIG": "Subir configuración", + "FILEUPLOADLABEL": "O cargar desde el archivo", + "CONFIG": "Config", + "YAMLCONFIG": "Yaml Config", + "CHOOSEFILE": "Elija el archivo", + "INVALIDCONFIG": "Configuración inválida", + "PAGE": { + "DASHBOARD": { + "DASHBOARD": "Tablero", + "RECENTUSERLOG": "Registro de usuario reciente", + "LOGS": "Troncos", + "FAILEDINSTANCES": "Instancias fallidas", + "NOINSTANCES": "Keine Instanzen verfügbar", + "UPTIME": "Tiempodeactividad", + "RUNNINGINSTANCES": "Corriendo Instancias", + "NETSLICETEMPLATE": "Plantilla NetSlice", + "NETSLICEINSTANCE": "NetSlice Instancias", + "USERS": "Usuarios", + "PROJECTS": "Proyectos", + "USERSETTINGS": "Usuarios Ajustes", + "LOGOUT": "Cerrar sesión" + }, + "LOGIN": { + "USERNAME": "Nombre de usuario", + "PASSWORD": "Contraseña", + "SIGNUP": "Regístrate", + "ACCOUNTCREATEMESSAGE": "¿No tienes una cuenta?", + "LOGIN": "iniciar sesión", + "PASSWORDVALIDMESSAGE": "se requiere contraseña", + "USERNAMEVALIDMESSAGE": "Se requiere nombre de usuario", + "SIGNINMSG": "Inicia sesión para iniciar tu sesión.", + "PASSWORDMINLENGTHVALIDMESSAGE": "La contraseña debe tener 8 caracteres y contiene al menos un carácter en mayúscula, minúscula, numérico y especial", + "USERNAMEMINLENGTHVALIDMESSAGE": "El nombre de usuario debe tener al menos 5 caracteres" + }, + "INSTANCEINSTANTIATE": { + "NEWINSTANCE": "Nueva instancia", + "NSNAME": "Nombre de Ns", + "DESCRIPTION": "Descripción", + "NSID": "Nsd Id", + "SSHKEY": "SSH Key", + "VIMACCOUNT": "Cuenta VIM", + "SSHKEYMSG": "Pega tu llave aquí" + }, + "NSMETRIC": { + "INSTANCESMETRIC": "Instancia métrica", + "METRICERROR": "Datos no disponibles en métricas" + }, + "USERSETTINGS": { + "LANGUAGE": "Idioma" + }, + "VIM": { + "CREATEDSUCCESSFULLY": "VIM Creada Exitosamente", + "LOACTIONINFO": "Escriba el nombre de la ubicación y haga clic en el botón Intro para seleccionar la ubicación de la lista" + }, + "VIMDETAILS": { + "NEWVIM": "Nuevo VIM", + "VIMACCOUNTDETAILS": "VIM Account detalles", + "NAME": "Nombre", + "VIMUSERNAME": "VIM Nombre de usuario", + "VIMURL": "VIM URL", + "VIMTYPE": "Tipo", + "TENANTNAME": "Tenant Nombre", + "DESCRIPTION": "Descripción", + "SCHEMATYPE": "Tipo de esquema", + "SCHEMAVERSION": "Versión de esquema", + "CONFIGPARAMETERS": "CONFIGURAR PARÁMETROS", + "SDNCONTROLLER": "SDN Controladora", + "SDNPORTMAPPING": "SDN La asignación de puertos", + "VIMNETWORKNAME": "VIM Nombre de red", + "SECURITYGROUPS": "Grupos de seguridad", + "AVAILABILITYZONE": "Zona de disponibilidad", + "REGIONALNAME": "Nombre de región", + "INSECURE": "Insegura", + "USEEXISTINGFLAVOURS": "Usa sabores existentes", + "USEINTERNALENDPOINT": "Usar punto final interno", + "APIVERSION": "Versión API ", + "PROJECTDOMAINID": "Proyecto dominio id", + "PROJECTDOMAINNAME": "Proyecto dominio name", + "USERDOMAINID": "Usuaria dominio id", + "USERDOMAINUSER": "Usuaria dominio nombre", + "KEYPAIR": "Par de claves", + "DATAPLANEPHYSICALNET": "Plano de datos physical net", + "USEFLOATINGIP": "Utilizar flotante ip", + "DATAPLANENETVLANRANGE": "Plano de datos net vlan range", + "MICROVERSION": "Microversión", + "BACKTOVIMACCOUNTS": "Atrás a VimAccounts", + "VIMPASSWORD": "VIM Contraseña", + "ADDITIONALCONFIG": "Adicional configuración", + "ADDITIONALCONFIGPLACEHOLDER": "{'key1': [...], 'key2': {}, 'key3': ''}", + "NEWVIMACCOUNT": "Nueva VIM Cuenta", + "ORGNAME": "Orgnombre", + "VCENTERIP": "Vcenter ip", + "VCENTERPORT": "Vcenter Puerto", + "ADMINUSERNAME": "Administración nombre de usuario", + "VCENTERUSER": "Vcenter usuaria", + "ADMINPASSWORD": "Administración contraseña", + "VCENTERPASSWORD": "Vcenter contraseña", + "NSXMANAGER": "Nsx gerente", + "VROPSSITE": "Vrops sitio", + "NSXUSER": "Nsx usuaria", + "VROPSUSER": "Vrops usuaria", + "NSXPASSWORD": "Nsx contraseña", + "VROPSPASSWORD": "Vrops contraseña", + "VPCCIDRBLOCK": "VPC cidr bloquear", + "FLAVORIINFO": "Flavor informacion", + "VIM_TYPE": "VIM Tipo", + "VIMLOCATION": "VIM Ubicación", + "SUBSCRIPTIONID": "ID de suscripción", + "RESOURCEGROUP": "Grupo de recursos", + "VNETNAME": "Nombre de red virtual", + "FLAVORSPATTERN": "Patrón de sabores" + }, + "WIMACCOUNTS": { + "CREATEDSUCCESSFULLY": "WIM Creado Exitosamente", + "WIMDETAILS": "Detalles de WIM", + "NEWWIM": "Nuevo WIM", + "SCHEMAVERSION": "Versión de esquema", + "RO": "RO", + "ROACCOUNT": "Cuenta RO", + "USERNAME": "Nombre de usuario de WIM", + "PASSWORD": "Contraseña WIM" + }, + "NSINSTANCE": { + "NEWNSINSTANCE": "Nueva NS", + "CREATEDSUCCESSFULLY": "NS Ejemplo Creado Exitosamente" + }, + "VNFINSTANCE": { + "ADDVNFINSTANCE": "Agregar instancia VNF" + }, + "PDUINSTANCE": { + "NEWPDUINSTANCE": "Nueva PDU", + "PDUTYPE": "Tipo de PDU", + "PARAMETERS": "Parámetros de instancias de PDU", + "ADDINSTANCEPARAMS": "Agregar parámetros", + "CREATEDSUCCESSFULLY": "Instancias de PDU creadas con éxito" + }, + "NETSLICEINSTANCE": { + "CREATENETSLICEINSTANCE": "Crear NSI" + }, + "SDNCONTROLLER": { + "NEWSDNCONTROLLER": "Nuevo controlador SDN", + "REGISTEREDSDNCONTROLLER": "Controladores SDN registrados", + "RO": "RO", + "DPID": "DPID", + "CREATEDSUCCESSFULLY": "SDN registrado con éxito", + "DPIDPLACEHOLDER": "xx:xx:xx:xx:xx:xx:xx:xx", + "DETAILS": "Detalles del controlador SDN" + }, + "USERS": { + "CREATEUSER": "Crear usuario", + "NEWUSER": "Nuevo usuario", + "USERNAME": "Nombre de usuario", + "PASSWORD": "Contraseña", + "CONFPASSWORD": "confirmar Contraseña", + "EDITUSER": "editar usuario", + "NEWPASSWORD": "Nueva contraseña", + "DEFAULTPROJECT": "Proyecto predeterminado", + "PASSWORDCONFLICT": "La contraseña y la contraseña de confirmación no coinciden", + "PASSWORDMATCH": "Contraseña", + "CREATEDSUCCESSFULLY": "Usuario Creada Exitosamente", + "EDITEDSUCCESSFULLY": "Usuario editado correctamente", + "EDITCREDENTIALS": "Cambia la contraseña", + "EDITUSERNAME": "Cambie el nombre de usuario", + "PROJECTSROLES": "Roles de proyectos", + "EDITPROJECTROLEMAPPING": "Editar asignación de roles de proyecto", + "ADDMAPPINGS": "Agregar asignaciones", + "EDITPROJECTROLEERROR": "Proporcione al menos un mapeo de roles del proyecto para continuar" + }, + "TOPOLOGY": { + "SELECTELEMENT": "Seleccionar elemento", + "VL": "VL", + "VNF": "VNF", + "VNFD": "VNFD", + "CP": "CP", + "NSD": "NSD", + "NS": "NS", + "VIRTUALLINK": "Enlace virtual", + "CONNECTIONPOINT": "Punto de conexión", + "INTCONNECTIONPOINT": "Int Punto de conexión", + "LINK": "Enlazar", + "ADDINGCP": "Seleccione un punto de conexión de {{vnfdname}} para vincular {{vlname}}?", + "INFO": "Informacion", + "HELP": "Ayuda", + "HELPINFO": { + "CREATEEDGE": "Crear borde", + "CREATEEDGEFIRSTSETENCE": "Seleccione el primer vértice haciendo clic en él usando", + "CREATEEDGESECONDSETENCE": "en otro vértice (diferente al seleccionado).", + "DELETEEDGEVERTEX": "Eliminar borde/vértice", + "DELETEEDGEVERTEXSENTENCE": "Doble clic en borde / vértice.", + "SPREADEDGE": "Borde extendido", + "SPREADEDGESENTENCE": "Seleccione el vértice haciendo clic en él usando", + "EDGEINFO": "Mostrar información de borde", + "EDGEINFOSENTENCE": "Seleccione el borde haciendo clic. La información se mostrará en el lado izquierdo." + }, + "VDU": "VDU", + "INTVL": "IntVL", + "INTCP": "IntCP", + "DATAEMPTY": "Por favor cambia algo" + }, + "PROJECT": { + "NEWPROJECT": "Nuevo proyecto", + "CREATEDSUCCESSFULLY": "Proyecto Creada Exitosamente", + "UPDATEDSUCCESSFULLY": "Proyecto Actualizada Exitosamente" + }, + "NSPACKAGE": { + "ADDNSPACKAGE": "Componer un nuevo NS", + "CREATEDSUCCESSFULLY": "NS Paquetes Creada Exitosamente", + "NSCOMPOSE": { + "UPDATEDSUCCESSFULLY": "Actualizada Exitosamente", + "CONFIRMCONNECTIONPOINT": "Confirme para agregar un punto de conexión", + "CANNOTLINKVNF": "No puedes vincular un vnf con un vnf", + "CANNOTLINKVL": "No puedes vincular un VL con un VL", + "CANNOTLINKVLVNF": "No puedes vincular un VL con un vnf", + "CANNOTLINKVNFCP": "No puedes vincular un VNF con un CP", + "CANNOTLINKVLCP": "No puedes vincular un VL con un CP", + "CANNOTLINKCP": "No puedes vincular un CP con un CP", + "ADDNSD": "Enlace virtual se agrega con éxito", + "ADDVNFD": "VNFD se agrega con éxito", + "ADDNS": "El enlace de conexión se agregó con éxito", + "DELETENSD": "Se eliminó con éxito el enlace virtual", + "DELETEVNFD": "Eliminado con éxito el VNF", + "DELETENS": "Se eliminó correctamente el punto de conexión", + "DELETELINK": "Se eliminó el enlace correctamente", + "MGMTNETWORK": "Mgmt Network", + "VIMNETWORKNAME": "Vim Network Name", + "MEMBER-VNF-INDEX": "member-vnf-index", + "VNFD-ID-REF": "vnfd-id-ref", + "VLD-ID": "vld-id", + "VNFD-CP-REF": "vnfd-punto de conexión-ref" + }, + "EDITPACKAGES": { + "UPDATEDSUCCESSFULLY": "Descriptor actualizado exitosamente" + } + }, + "VNFPACKAGE": { + "ADDVNFPACKAGE": "Componer un nuevo VNF", + "CREATEDSUCCESSFULLY": "VNF Paquetes Creada Exitosamente", + "VNFCOMPOSE": { + "UPDATEDSUCCESSFULLY": "Actualizada Exitosamente", + "INVALIDSELECTION": "Selección invalida", + "YOUCANNOTDELETELINK": "No puedes eliminar el enlace", + "CANNOTLINKVDUANDINTCP": "No puedes vincular vdu con int_cp", + "CANNOTLINKINTCPANDVDU": "No puedes vincular int_cp con vdu", + "CANNOTLINKCPANDVNFVL": "No puedes vincular cp con vnf_vl", + "CANNOTLINKVNFVLANDCP": "No puedes vincular vnf_vl con cp", + "CANNOTLINKINTCPANDCP": "No puedes vincular intcp con cp", + "CANNOTLINKCPANDINTCP": "No puedes vincular cp con int_cp", + "CANNOTLINKVDUANDVDU": "No puedes vincular un vdu con un vdu" + } + }, + "NETSLICE": { + "CREATEDSUCCESSFULLY": "Netslice Creada Exitosamente", + "TEMPLATECREATEDSUCCESSFULLY": "Netslice Modelo Created Successfully", + "UPDATEDSUCCESSFULLY": "Plantilla actualizada con éxito" + }, + "NETSLICETEMPLATE": { + "NETSLICETEMPLATEDETAILS": "Red Rebanadas Modelo Detalles" + }, + "NSTINSTANCEINSTANTIATE": { + "NEWINSTANCE": "Nueva instancia", + "NSNAME": "Nombre de Ns", + "DESCRIPTION": "Descripción", + "NSTID": "Nsd Id", + "SSHKEY": "SSH Key", + "VIMACCOUNT": "Cuenta VIM", + "SSHKEYMSG": "Pega tu llave aquí ..." + }, + "NSPRIMITIVE": { + "PRIMITIVE": "Primitiva", + "PRIMITIVEPARAMETERS": "Primitiva Parámetros", + "ADDPRIMITIVEPARAMS": "Añadir Parámetros primitivos", + "EXECUTEDSUCCESSFULLY": "Ejecutada NS Configuración primitiva" + }, + "ROLES": { + "CREATEROLE": "Crear rol", + "ROLE": "Papel", + "PERMISSIONS": "Permisos", + "YAMLPERMISSIONS": "YAML Permisos", + "CREATEDSUCCESSFULLY": "Rol creado con éxito", + "UPDATEDSUCCESSFULLY": "Rol actualizado exitosamente", + "ROLEJSONERROR": "Los permisos de rol deben proporcionarse de manera clave-valor", + "ROLEKEYERROR": "El valor de '{{roleKey}}' en los permisos de un rol debe ser booleano", + "EDITROLE": "Editar rol", + "PREVIEW": "Avance", + "TEXTVIEW": "Vista de texto" + }, + "K8S": { + "MENUK8S": "K8s", + "MENUK8SCLUSTER": "Clusters K8s", + "MENUK8SREPO": "K8s Repos", + "REGISTERK8CLUSTER": "Grupos de K8 registrados", + "ADDK8CLUSTER": "Agregar clúster K8s", + "K8SCLUSTERDETAILS": "Detalles del clúster K8s", + "REGISTERK8REPO": "Repositorio K8 registrado", + "ADDK8REPO": "Agregar repositorio K8s", + "K8SREPODETAILS": "Detalles del repositorio de K8s", + "CREATEDSUCCESSFULLY": "K8 creados con éxito", + "NEWK8SCLUSTER": "Nuevo clúster K8s", + "NAME": "Nombre", + "K8SVERSION": "Versión K8s", + "VIMACCOUNT": "Cuenta Vim", + "DESCRIPTION": "Descripción", + "NETS": "Nets", + "NETSPLACEHOLDER": "example: {'net1': 'osm-ext'}", + "CREDENTIALS": "Cartas credenciales", + "NEWK8SREPO": "Nuevo repositorio K8s", + "TYPE": "Tipo", + "URL": "URL" + } + }, + "HTTPERROR": { + "401": "Acceso denegado", + "400": "Por favor revise la solicitud e intente nuevamente", + "404": "El servicio esperado no está disponible. Vuelve a intentarlo más tarde.", + "500": "Error del servidor. Vuelve a intentarlo más tarde", + "502": "Bad Gateway, por favor intente nuevamente más tarde", + "503": "El servicio no está disponible temporalmente. Vuelve a intentarlo más tarde", + "504": "Error de tiempo de espera de la puerta de enlace. Vuelva a intentarlo más tarde", + "409": "Por favor, inténtelo de nuevo más tarde" + }, + "PAGENOTFOUND": { + "OOPS": "¡Uy!", + "NOTFOUND": "404 No encontrado", + "CONTENT": "La página no se puede encontrar o no está autorizada, puede que ya no sea relevante o se haya cambiado su nombre.", + "MEAN": "Mientras tanto, puede volver a", + "HOME": "Hogar" + }, + "DOMVALIDATIONS": { + "INVALIDURL": "Ingrese una URL válida", + "INVALIDIPADDRESS": "Ingrese una dirección IP válida", + "INVALIDPORTADDRESS": "Ingrese una dirección de puerto válida", + "INVALIDDPID": "Ingrese un DPID válido", + "INVALIDJSON": "Ingrese un formato JSON válido", + "INVALIDYAML": "Ingrese un formato YAML válido" + }, + "GRAFANA": { + "METRICSERROR": "No es una métrica válida" + } +} \ No newline at end of file diff --git a/src/assets/i18n/pt.json b/src/assets/i18n/pt.json new file mode 100644 index 0000000..3c948ac --- /dev/null +++ b/src/assets/i18n/pt.json @@ -0,0 +1,479 @@ +{ + "OSM": "OSM", + "APPVERSION": "Versão da aplicação", + "OSMVERSION": "Versão OSM", + "OSMSOURCEMANO": "Código aberto MANO", + "ADMIN": "Admin", + "ENTRIES": "Entradas", + "COMPOSE": "Componha um", + "CREATE": "Crio", + "SELECT": "Selecione", + "CANCEL": "Cancelar", + "SAVE": "Salve", + "COUNT": "contagem", + "IMAGE": "Imagem", + "IPPROFILEREF": "Ref do perfil IP", + "ACTION": "Açao", + "ADD": "Adicionar", + "EDIT": "Editar", + "APPLY": "Aplique", + "FORCE": "Força", + "DOWNLOAD": "Baixar", + "CONTENT": "Conteúdo", + "DELETE": "Excluir", + "FORCEDELETE": "Forçar exclusão", + "RENAME": "Renomear", + "INFO": "Info", + "NSPACKAGES": "Pacotes NS", + "VNFPACKAGES": "Pacotes VNF", + "INSTANCES": "Instâncias", + "INSTANTIATE": "Instanciar", + "NSINSTANCES": "Instâncias NS", + "VNFINSTANCES": "Instâncias VNF", + "PDUINSTANCES": "Instâncias da PDU", + "VIMACCOUNTS": "Contas VIM", + "WIMACCOUNTS": "Contas WIM", + "SDNCONTROLLER": "Controlador SDN", + "NETSLICE": "Netslice", + "PROJECT": "Projeto", + "DOMAIN": "Domínio", + "MODIFIED": "Modificada", + "PACKAGES": "Pacotes", + "NODATAMSG": "Sem dados disponíveis na tabela", + "SHORTNAME": "Nome curto", + "IDENTIFIER": "Identificador", + "DESCRIPTION": "Descrição", + "VENDOR": "Fornecedor", + "VERSION": "Versão", + "ACTIONS": "Ações", + "NAME": "Nome", + "USAGESTATE": "UsageState", + "MODIFICATIONDATE": "Modificação de data", + "CREATEDDATE": "Data de criação", + "OPERATIONALSTATUS": "Estado operacional", + "OPERATIONALSTATE": "Estado operacional", + "CONFIGSTATUS": "Status da configuração", + "DETAILEDSTATUS": "Status detalhado", + "NSDNAME": "Nome nsd", + "NSTNAME": "Nst name", + "TYPE": "Tipo", + "VNFD": "VNFD", + "VNF": "VNF", + "MEMBERINDEX": "Índice de membros", + "NS": "NS", + "CREATEDAT": "Criado em", + "CREATED": "Criada", + "ALL": "Todos", + "ID": "Identidade", + "OPERATIONSTATE": "Estado da operação", + "STARTTIME": "Hora de início", + "STATUSENTEREDTIME": "Horário de entrada do status", + "HISTORYOFOPERATIONS": "História das Operações", + "UPDATE": "Atualizar", + "READONLYMODE": "Modo somente leitura", + "CURRENTLY": "Atualmente", + "ON": "Em", + "OFF": "Fora", + "IN": "em", + "FILES": "arquivos", + "NEW": "Nova", + "RECENTLY": "Recentemente", + "TOPOLOGY": "Topologia", + "PLEASEWAIT": "Por favor, espere", + "LOADING": "Carregando", + "RESOURCEORCHESTRATOR": "Orquestrador de Recursos", + "VIEW": "Visão", + "DROP": "Solta", + "HERE": "Aqui", + "MAPVIEW": "Visão do mapa", + "LISTVIEW": "Exibição de lista", + "OK": "Está bem", + "DELETEDSUCCESSFULLY": "Apagado com sucesso", + "SESSIONEXPIRY": "Sessão expirada, faça o login novamente", + "DELETECONFIRMPOPUPMESSAGE": "Tem certeza de que deseja excluir", + "DELETELOADERMESSAGE": "Aguarde enquanto a exclusão está em andamento", + "VALUE": "Valor", + "PERFORMACTION": "Executar a ação", + "EXECUTE": "Executar", + "EXECNSPRIMITIVE": "Exec NS Primitive", + "PRIMITIVETYPE": "Tipo primitivo", + "VNFPRIMITIVE": "Primitivo de nível VNF", + "NSPRIMITIVE": "NS Level Primitive", + "DESCRIPTOR": "Descritora", + "ERROR": "Alguma coisa deu errado. Por favor tente outra vez", + "SHOWGRAPH": "Mostrar gráfico", + "UPDATESHOWGRAPH": "Atualizar e mostrar gráfico", + "CREATEPACKAGE": "Criar novo pacote", + "GZFILETYPEERRROR": "Carregue apenas o arquivo tar.gz e o tamanho não deve exceder 15 MB", + "YAMLFILETYPEERRROR": "Carregar apenas arquivo YAML e o tamanho não deve exceder 15 MB", + "JSONFILETYPEERRROR": "Carregar apenas arquivo JSON e o tamanho não deve exceder 15 MB", + "PUBFILETYPEERRROR": "Carregar apenas arquivo PUB e o tamanho não deve exceder 15 MB", + "PACKAGE": "Pacote", + "URL": "URL", + "DEPLOYED": "Deployed", + "ROLES": "Funções", + "INSTANCEDETAILS": "Detalhes da Instância", + "IPADDRESS": "Endereço de IP", + "MGMT": "Mgmt", + "NETNAME": "Nome líquido", + "USER": "Do utilizador", + "PORT": "Porta", + "USERNAME": "Nome de usuário", + "PASSWORD": "Senha", + "NODATAERROR": "Ocorreu um erro ao recuperar as informações", + "FREEZE": "Congelar", + "UNFREEZE": "Descongelar", + "CLONE": "Clone", + "CLONECONFIRMPOPUPMESSAGE": "Tem certeza de que deseja clonar", + "CLONESUCCESSFULLY": "Pacote clonado com sucesso", + "DROPFILES": "Basta arrastar e soltar arquivos aqui ou clique aqui para fazer upload de arquivos", + "DROPFILESVALIDATION": "Selecione um arquivo para processar", + "METRICS": "Métricas", + "NOOFHOURS": "Não de horas", + "MANDATORYCHECK": "Os campos obrigatórios estão marcados com um asterisco (*)", + "K8VERSION": "Versão K8", + "ENTER": "Entrar", + "SWITCHPROJECT": "Switch Project", + "CURRENTPROJECT": "Projeto atual", + "SUBMIT": "Enviar", + "REFRESH": "Atualizar", + "OPEN": "Aberto", + "UPLOADCONFIG": "Upload Config", + "FILEUPLOADLABEL": "Ou carregue do arquivo", + "CONFIG": "Config", + "YAMLCONFIG": "Yaml Config", + "CHOOSEFILE": "Escolher arquivo", + "INVALIDCONFIG": "Configuração inválida", + "PAGE": { + "DASHBOARD": { + "DASHBOARD": "painel de controle", + "RECENTUSERLOG": "Registro recente do usuário", + "LOGS": "Logs", + "FAILEDINSTANCES": "Instâncias com falha", + "NOINSTANCES": "Keine Instanzen verfügbar", + "UPTIME": "Tempo de atividade", + "RUNNINGINSTANCES": "Instâncias em execução", + "NETSLICETEMPLATE": "Modelo de fatia líquida", + "NETSLICEINSTANCE": "Instâncias de fatia líquida", + "USERS": "Comercial", + "PROJECTS": "Projetos", + "USERSETTINGS": "Configurações do usuário", + "LOGOUT": "Sair" + }, + "LOGIN": { + "USERNAME": "Nome de usuário", + "PASSWORD": "Senha", + "SIGNUP": "Inscrever-se", + "ACCOUNTCREATEMESSAGE": "Não possui uma conta?", + "LOGIN": "entrar", + "PASSWORDVALIDMESSAGE": "Senha requerida", + "USERNAMEVALIDMESSAGE": "Nome de usuário é requerido", + "SIGNINMSG": "Faça login para iniciar sua sessão", + "PASSWORDMINLENGTHVALIDMESSAGE": "A senha deve ter 8 caracteres e conter pelo menos um caractere maiúsculo, minúsculo, numérico e especial", + "USERNAMEMINLENGTHVALIDMESSAGE": "O nome de usuário deve ter pelo menos 5 caracteres" + }, + "INSTANCEINSTANTIATE": { + "NEWINSTANCE": "Nova Instância", + "NSNAME": "Ns Name", + "DESCRIPTION": "Descrição", + "NSID": "ID Nsd", + "SSHKEY": "Chave SSH", + "VIMACCOUNT": "Conta VIM", + "SSHKEYMSG": "Cole sua chave aqui" + }, + "NSMETRIC": { + "INSTANCESMETRIC": "Métrica de Instâncias", + "METRICERROR": "Dados não disponíveis em métricas" + }, + "USERSETTINGS": { + "LANGUAGE": "Língua" + }, + "VIM": { + "CREATEDSUCCESSFULLY": "VIM criado com sucesso", + "LOACTIONINFO": "Digite o nome do local e clique no botão Enter para selecionar o local na lista" + }, + "VIMDETAILS": { + "NEWVIM": "Novo VIM", + "VIMACCOUNTDETAILS": "Detalhes da conta VIM", + "NAME": "Nome", + "VIMUSERNAME": "Nome de usuário do VIM", + "VIMURL": "URL do VIM", + "VIMTYPE": "Tipo", + "TENANTNAME": "Nome do inquilino", + "DESCRIPTION": "Descrição", + "SCHEMATYPE": "Tipo de esquema", + "SCHEMAVERSION": "Versão do esquema", + "CONFIGPARAMETERS": "PARÂMETROS CONFIG", + "SDNCONTROLLER": "Controlador SDN", + "SDNPORTMAPPING": "Mapeamento de porta SDN", + "VIMNETWORKNAME": "Nome da rede VIM", + "SECURITYGROUPS": "Grupos de Segurança", + "AVAILABILITYZONE": "Zona de disponibilidade", + "REGIONALNAME": "Nome da região", + "INSECURE": "Insegura", + "USEEXISTINGFLAVOURS": "Use sabores existentes", + "USEINTERNALENDPOINT": "Usar terminal interno", + "APIVERSION": "Versão da API", + "PROJECTDOMAINID": "ID do domínio do projeto", + "PROJECTDOMAINNAME": "Nome de domínio do projeto", + "USERDOMAINID": "ID do domínio do usuário", + "USERDOMAINUSER": "Nome de domínio do usuário", + "KEYPAIR": "Par de chaves", + "DATAPLANEPHYSICALNET": "Rede física do plano de dados", + "USEFLOATINGIP": "Use ip flutuante", + "DATAPLANENETVLANRANGE": "Dataplane net vlan range", + "MICROVERSION": "Microversão", + "BACKTOVIMACCOUNTS": "Voltar para VimAccounts", + "VIMPASSWORD": "Senha do VIM", + "ADDITIONALCONFIG": "Configuração adicional", + "ADDITIONALCONFIGPLACEHOLDER": "{'key1':[...],'key2':{},'key3':''}", + "NEWVIMACCOUNT": "Nova conta VIM", + "ORGNAME": "Orgnome", + "VCENTERIP": "Vcenter ip", + "VCENTERPORT": "Porta Vcenter", + "ADMINUSERNAME": "Nome de usuário do administrador", + "VCENTERUSER": "Usuário do Vcenter", + "ADMINPASSWORD": "senha do administrador", + "VCENTERPASSWORD": "Senha do Vcenter", + "NSXMANAGER": "Gerente Nsx", + "VROPSSITE": "Site Vrops", + "NSXUSER": "Usuário Nsx", + "VROPSUSER": "Usuário Vrops", + "NSXPASSWORD": "Senha Nsx", + "VROPSPASSWORD": "Senha Vrops", + "VPCCIDRBLOCK": "Bloco cidr VPC", + "FLAVORIINFO": "Informação do sabor", + "VIM_TYPE": "Tipo VIM", + "VIMLOCATION": "Localização do VIM", + "SUBSCRIPTIONID": "ID de Inscrição", + "RESOURCEGROUP": "Grupo de Recursos", + "VNETNAME": "Nome da VNet", + "FLAVORSPATTERN": "Padrão de sabores" + }, + "WIMACCOUNTS": { + "CREATEDSUCCESSFULLY": "WIM criado com sucesso", + "WIMDETAILS": "Detalhes do WIM", + "NEWWIM": "Novo WIM", + "SCHEMAVERSION": "Versão do esquema", + "RO": "RO", + "ROACCOUNT": "Conta RO", + "USERNAME": "Nome de usuário WIM", + "PASSWORD": "Senha WIM" + }, + "NSINSTANCE": { + "NEWNSINSTANCE": "Nova NS", + "CREATEDSUCCESSFULLY": "Instância NS criada com êxito" + }, + "VNFINSTANCE": { + "ADDVNFINSTANCE": "Adicionar instância VNF" + }, + "PDUINSTANCE": { + "NEWPDUINSTANCE": "Nova PDU", + "PDUTYPE": "Tipo de PDU", + "PARAMETERS": "Parâmetros de instâncias da PDU", + "ADDINSTANCEPARAMS": "Adicionar parâmetros", + "CREATEDSUCCESSFULLY": "Instâncias da PDU criadas com êxito" + }, + "NETSLICEINSTANCE": { + "CREATENETSLICEINSTANCE": "Criar NSI" + }, + "SDNCONTROLLER": { + "NEWSDNCONTROLLER": "Novo controlador SDN", + "REGISTEREDSDNCONTROLLER": "Controladores SDN registrados", + "RO": "RO", + "DPID": "DPID", + "CREATEDSUCCESSFULLY": "SDN Registrado com Sucesso", + "DPIDPLACEHOLDER": "xx:xx:xx:xx:xx:xx:xx:xx", + "DETAILS": "Detalhes do Controlador SDN" + }, + "USERS": { + "CREATEUSER": "Criar usuário", + "NEWUSER": "Novo usuário", + "USERNAME": "Nome de Usuário", + "PASSWORD": "Senha", + "CONFPASSWORD": "Confirme a Senha", + "EDITUSER": "Editar usuário", + "NEWPASSWORD": "Nova senha", + "DEFAULTPROJECT": "Projeto Padrão", + "PASSWORDCONFLICT": "Senha e confirmação de senha não correspondem", + "PASSWORDMATCH": "Correspondência de senha", + "CREATEDSUCCESSFULLY": "Usuário criado com sucesso", + "EDITEDSUCCESSFULLY": "Edição do Usuário com Sucesso", + "EDITCREDENTIALS": "Mudar senha", + "EDITUSERNAME": "Mudar nome de usuário", + "PROJECTSROLES": "Funções dos Projetos", + "EDITPROJECTROLEMAPPING": "Editar mapeamento de função do projeto", + "ADDMAPPINGS": "Adicionar mapeamentos", + "EDITPROJECTROLEERROR": "Forneça pelo menos um mapeamento de função do projeto para continuar" + }, + "TOPOLOGY": { + "SELECTELEMENT": "Selecionar elemento", + "VL": "VL", + "VNF": "VNF", + "VNFD": "VNFD", + "CP": "CP", + "NSD": "NSD", + "NS": "NS", + "VIRTUALLINK": "Link virtual", + "CONNECTIONPOINT": "Ponto de conexão", + "INTCONNECTIONPOINT": "Ponto de conexão int", + "LINK": "Ligação", + "ADDINGCP": "Por favor, selecione um ponto de conexão {{vnfdname}} para vincular {{vlname}}?", + "INFO": "Info", + "HELP": "Socorro", + "HELPINFO": { + "CREATEEDGE": "Criar aresta", + "CREATEEDGEFIRSTSETENCE": "Selecione o primeiro vértice clicando nele usando", + "CREATEEDGESECONDSETENCE": "em outro vértice (diferente do selecionado).", + "DELETEEDGEVERTEX": "Excluir aresta / vértice", + "DELETEEDGEVERTEXSENTENCE": "Clicar duas vezes na aresta / vértice.", + "SPREADEDGE": "Espalhe a borda", + "SPREADEDGESENTENCE": "Selecione o vértice clicando nele usando", + "EDGEINFO": "Mostrar informações da aresta", + "EDGEINFOSENTENCE": "Selecione a aresta clicando. A informação será mostrada no lado esquerdo." + }, + "VDU": "VDU", + "INTVL": "IntVL", + "INTCP": "IntCP", + "DATAEMPTY": "Por favor mude algo" + }, + "PROJECT": { + "NEWPROJECT": "Novo projeto", + "CREATEDSUCCESSFULLY": "Projeto criado com sucesso", + "UPDATEDSUCCESSFULLY": "Projeto atualizado com sucesso" + }, + "NSPACKAGE": { + "ADDNSPACKAGE": "Componha um novo NS", + "CREATEDSUCCESSFULLY": "Pacote NS criado com sucesso", + "NSCOMPOSE": { + "UPDATEDSUCCESSFULLY": "Atualizado com sucesso", + "CONFIRMCONNECTIONPOINT": "Confirme para adicionar o ponto de conexão", + "CANNOTLINKVNF": "Você não pode vincular um vnf a um vnf", + "CANNOTLINKVL": "Você não pode vincular uma VL a uma VL", + "CANNOTLINKVLVNF": "Você não pode vincular um VL a um vnf", + "CANNOTLINKVNFCP": "Você não pode vincular um VNF a um CP", + "CANNOTLINKVLCP": "Você não pode vincular uma VL a um CP", + "CANNOTLINKCP": "Você não pode vincular um CP a um CP", + "ADDNSD": "Link virtual adicionado com sucesso", + "ADDVNFD": "VNFD é adicionado com sucesso", + "ADDNS": "Link de conexão adicionado com sucesso", + "DELETENSD": "O link virtual foi excluído com sucesso", + "DELETEVNFD": "O VNF foi excluído com sucesso", + "DELETENS": "Excluído com êxito o ponto de conexão", + "DELETELINK": "O link foi excluído com sucesso", + "MGMTNETWORK": "Mgmt Network", + "VIMNETWORKNAME": "Vim Network Name", + "MEMBER-VNF-INDEX": "member-vnf-index", + "VNFD-ID-REF": "vnfd-id-ref", + "VLD-ID": "vld-id", + "VNFD-CP-REF": "vnfd-ponto de conexão-ref" + }, + "EDITPACKAGES": { + "UPDATEDSUCCESSFULLY": "Atualizado com sucesso" + } + }, + "VNFPACKAGE": { + "ADDVNFPACKAGE": "Componha um novo VNF", + "CREATEDSUCCESSFULLY": "Pacote VNF criado com sucesso", + "VNFCOMPOSE": { + "UPDATEDSUCCESSFULLY": "Atualizado com sucesso", + "INVALIDSELECTION": "Seleção inválida", + "YOUCANNOTDELETELINK": "Você não pode excluir o link", + "CANNOTLINKVDUANDINTCP": "Você não pode vincular o vdu ao int_cp", + "CANNOTLINKINTCPANDVDU": "Você não pode vincular int_cp ao vdu", + "CANNOTLINKCPANDVNFVL": "Você não pode vincular o cp ao vnf_vl", + "CANNOTLINKVNFVLANDCP": "Você não pode vincular vnf_vl ao cp", + "CANNOTLINKINTCPANDCP": "Você não pode vincular o intcp ao cp", + "CANNOTLINKCPANDINTCP": "Você não pode vincular o cp ao int_cp", + "CANNOTLINKVDUANDVDU": "Você não pode vincular um vdu a um vdu" + } + }, + "NETSLICE": { + "CREATEDSUCCESSFULLY": "Netslice criado com sucesso", + "TEMPLATECREATEDSUCCESSFULLY": "Modelo Netslice criado com sucesso", + "UPDATEDSUCCESSFULLY": "Modelo atualizado com sucesso" + }, + "NETSLICETEMPLATE": { + "NETSLICETEMPLATEDETAILS": "Detalhes do modelo de fatias de rede" + }, + "NSTINSTANCEINSTANTIATE": { + "NEWINSTANCE": "Nova Instância", + "NSNAME": "Ns Name", + "DESCRIPTION": "Descrição", + "NSTID": "Nst Id", + "SSHKEY": "Chave SSH", + "VIMACCOUNT": "Conta VIM", + "SSHKEYMSG": "Cole sua chave aqui ..." + }, + "NSPRIMITIVE": { + "PRIMITIVE": "Primitiva", + "PRIMITIVEPARAMETERS": "Parâmetros primitivos", + "ADDPRIMITIVEPARAMS": "Adicionar Parâmetros Primitivos", + "EXECUTEDSUCCESSFULLY": "Configuração primitiva do NS executada" + }, + "ROLES": { + "CREATEROLE": "Criar função", + "ROLE": "Função", + "PERMISSIONS": "Permissões", + "YAMLPERMISSIONS": "YAML Permissões", + "CREATEDSUCCESSFULLY": "Função criada com sucesso", + "UPDATEDSUCCESSFULLY": "Função atualizada com sucesso", + "ROLEJSONERROR": "As permissões de função devem ser fornecidas de maneira com valor-chave", + "ROLEKEYERROR": "O valor de '{{roleKey}}' nas permissões de uma função deve ser booleano", + "EDITROLE": "Editar função", + "PREVIEW": "Pré-visualização", + "TEXTVIEW": "Visualização de texto" + }, + "K8S": { + "MENUK8S": "K8s", + "MENUK8SCLUSTER": "Clusters do K8s", + "MENUK8SREPO": "Repos do K8s", + "REGISTERK8CLUSTER": "Clusters K8s registrados", + "ADDK8CLUSTER": "Adicionar cluster do K8s", + "K8SCLUSTERDETAILS": "Detalhes do cluster K8s", + "REGISTERK8REPO": "Repositório registrado do K8s", + "ADDK8REPO": "Adicionar repositório K8s", + "K8SREPODETAILS": "Detalhes do Repositório do K8s", + "CREATEDSUCCESSFULLY": "K8s criado com sucesso", + "NEWK8SCLUSTER": "Novo Cluster K8s", + "NAME": "Nome", + "K8SVERSION": "Versão do K8s", + "VIMACCOUNT": "Conta Vim", + "DESCRIPTION": "Descrição", + "NETS": "Nets", + "NETSPLACEHOLDER": "example: {'net1': 'osm-ext'}", + "CREDENTIALS": "Credenciais", + "NEWK8SREPO": "Novo Repositório do K8s", + "TYPE": "Tipo", + "URL": "URL" + } + }, + "HTTPERROR": { + "401": "Acesso negado", + "400": "Verifique a solicitação e tente novamente", + "404": "Serviço esperado não disponível, tente novamente mais tarde", + "500": "Erro no servidor, tente novamente mais tarde", + "502": "Gateway incorreto. Tente novamente mais tarde", + "503": "Serviço temporariamente indisponível. Tente novamente mais tarde", + "504": "Erro de tempo limite do gateway. Tente novamente mais tarde", + "409": "Por favor, tente novamente mais tarde" + }, + "PAGENOTFOUND": { + "OOPS": "Opa!", + "NOTFOUND": "404 não encontrado", + "CONTENT": "A página não pode ser encontrada ou não autorizada, pode não ser mais relevante ou seu nome foi alterado", + "MEAN": "Enquanto isso, você pode voltar para", + "HOME": "Casa" + }, + "DOMVALIDATIONS": { + "INVALIDURL": "Digite um URL válido", + "INVALIDIPADDRESS": "Digite um endereço IP válido", + "INVALIDPORTADDRESS": "Digite um endereço PORT válido", + "INVALIDDPID": "Digite um DPID válido", + "INVALIDJSON": "Digite um formato JSON válido", + "INVALIDYAML": "Digite um formato YAML válido" + }, + "GRAFANA": { + "METRICSERROR": "Métricas não válidas" + } +} \ No newline at end of file diff --git a/src/assets/images/CP-VNF.svg b/src/assets/images/CP-VNF.svg new file mode 100644 index 0000000..26163da --- /dev/null +++ b/src/assets/images/CP-VNF.svg @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/CP.svg b/src/assets/images/CP.svg new file mode 100644 index 0000000..7ed1b03 --- /dev/null +++ b/src/assets/images/CP.svg @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/INTCP.svg b/src/assets/images/INTCP.svg new file mode 100644 index 0000000..773134a --- /dev/null +++ b/src/assets/images/INTCP.svg @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/INTVL.svg b/src/assets/images/INTVL.svg new file mode 100644 index 0000000..33fcce3 --- /dev/null +++ b/src/assets/images/INTVL.svg @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/TICK.svg b/src/assets/images/TICK.svg new file mode 100644 index 0000000..4675aad --- /dev/null +++ b/src/assets/images/TICK.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/VDU.svg b/src/assets/images/VDU.svg new file mode 100644 index 0000000..7dc6540 --- /dev/null +++ b/src/assets/images/VDU.svg @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/VL.svg b/src/assets/images/VL.svg new file mode 100644 index 0000000..6361c01 --- /dev/null +++ b/src/assets/images/VL.svg @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/VNFD.svg b/src/assets/images/VNFD.svg new file mode 100644 index 0000000..86b7fc8 --- /dev/null +++ b/src/assets/images/VNFD.svg @@ -0,0 +1,26 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/login_background.jpg b/src/assets/images/login_background.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dc1411d974852d01e9929da808b831cd55617214 GIT binary patch literal 768110 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

>d=#NwPM+}R{RDb>* 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

xVgB#m6{A( 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

Ji>vty4K0cf4Kb5p* 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