From d3a85af43fc2c019a42629d925b05596bb867c91 Mon Sep 17 00:00:00 2001 From: Vladimir Shakhov Date: Tue, 23 Oct 2018 16:53:12 +0700 Subject: [PATCH] VM logs (#1362) * refactor(vm): get rid of BaseModel for VM model (#1180) PR close #1104 * build(code-coverage): enable source-maps by default (#1347) This is a workaround to fix code coverage mapping * Update README.md * Create version * Update README.md * Update README.md * feat(navigation): rework navigation menu (#1333) PR close #1235 * feat(config-validation): add config validators (#1309) * feat(config-validation): add config validators * revert security group template interface * add more validation * remove allowReorderingSidenav validation, use uniqueItems * Update vm-colors.scheme.json * style: update app code style (#1359) * Add prettier * Prettier formatted * Work in progress * Fix tsc errors and lint errors * Ann few more rules and changes to codebase * Add precommit hook * Run prettier on entire codebase * Run prettier * Fix lint error * fix bug about grouping * Fix bugs * Remove unused files * fix(misc): semantic errors * fix(lint) --- .prettierignore | 2 + .prettierrc | 5 + README.md | 188 ++-- angular.json | 34 +- config-guide.md | 171 ++-- e2e/login.e2e-spec.ts | 19 +- e2e/pages/app.po.ts | 21 +- e2e/pages/login.po.ts | 28 +- e2e/pages/vm-list.po.ts | 21 +- e2e/protractor.conf.js | 14 +- package.json | 12 +- proxy-conf-example.js | 25 +- .../account-actions.container.ts | 22 +- .../account-creation.container.ts | 17 +- .../account-details.container.ts | 75 +- .../account-filter.container.ts | 105 ++- .../account-sidebar.container.ts | 19 +- .../account-user-edit.container.ts | 16 +- .../account-user-password.container.ts | 16 +- .../account-users.container.ts | 18 +- .../account-container/account.container.ts | 35 +- .../account-list-filter.component.html | 10 +- .../account-list-filter.component.ts | 47 +- .../account-list/account-list.component.ts | 25 +- .../account-page/account-page.component.html | 6 +- .../account-page/account-page.component.ts | 31 +- .../account-details.component.ts | 15 +- .../account-limits.component.ts | 19 +- .../account-configuration.component.html | 2 +- .../account-configuration.component.ts | 39 +- .../edit-account-configuration.component.ts | 8 +- .../account-settings.component.html | 2 +- .../account-settings.component.ts | 21 +- .../account-sidebar.component.ts | 15 +- .../account-statistics.component.ts | 27 +- .../account-user-card.component.ts | 6 +- .../account-user-edit.component.ts | 33 +- .../account-user-password.component.ts | 3 +- .../account-users.component.html | 8 +- .../account-users/account-users.component.ts | 62 +- .../account/account-item.component.spec.ts | 7 +- .../account/account/account-item.component.ts | 3 +- .../card-item/account-card-item.component.ts | 23 +- .../row-item/account-row-item.component.ts | 23 +- src/app/account/accounts.module.ts | 13 +- src/app/account/accounts.routing.ts | 18 +- .../account-creation-dialog.component.spec.ts | 59 +- .../account-creation-dialog.component.ts | 34 +- .../account-creation.component.ts | 32 +- src/app/app-routing.module.ts | 24 +- src/app/app.component.ts | 37 +- src/app/app.module.ts | 89 +- src/app/auth/auth.module.ts | 15 +- src/app/auth/login.component.ts | 40 +- src/app/auth/logout.component.ts | 8 +- src/app/auth/store/auth.actions.ts | 6 +- src/app/auth/store/auth.effects.ts | 15 +- src/app/core/components/index.ts | 1 - .../components/sidenav/_sidenav-theme.scss | 14 - .../components/sidenav/sidenav-animations.ts | 41 - .../core/components/sidenav/sidenav-routes.ts | 92 -- .../components/sidenav/sidenav.component.html | 103 --- .../components/sidenav/sidenav.component.scss | 157 ---- .../components/sidenav/sidenav.component.ts | 195 ----- .../config/config-validation.service.spec.ts | 19 +- .../core/config/config-validation.service.ts | 27 +- src/app/core/config/default-configuration.ts | 28 +- .../api-doc-link.scheme.json | 9 + .../configure-sidenav.scheme.json | 60 -- ...om-compute-offering-parameters.scheme.json | 10 +- .../core/config/validation-schemes/index.ts | 20 +- .../offering-compatibility-policy.scheme.json | 19 + .../security-group-templates.scheme.json | 112 +++ .../validation-schemes/vm-colors.scheme.json | 17 + src/app/core/core.module.ts | 48 +- .../components/app-nav/app-nav.component.html | 24 + .../components/app-nav/app-nav.component.scss | 28 + .../components/app-nav/app-nav.component.ts | 22 + src/app/core/nav-menu/components/index.ts | 4 + .../components/license/license.component.html | 7 + .../components/license/license.component.scss | 10 + .../components/license/license.component.ts | 15 + .../menu-header/menu-header.component.html | 12 + .../menu-header/menu-header.component.scss | 30 + .../menu-header/menu-header.component.ts | 18 + .../section-nav/section-nav.component.html | 18 + .../section-nav/section-nav.component.scss | 27 + .../section-nav/section-nav.component.ts | 21 + src/app/core/nav-menu/models/index.ts | 2 + .../core/nav-menu/models/route.interface.ts | 9 + .../nav-menu/models/subroute.interface.ts | 6 + src/app/core/nav-menu/nav-menu-theme.scss | 18 + .../core/nav-menu/nav-menu.service.spec.ts | 72 ++ src/app/core/nav-menu/nav-menu.service.ts | 57 ++ .../nav-menu/routes/accounts-subroutes.ts | 22 + .../core/nav-menu/routes/app-nav-routes.ts | 20 + src/app/core/nav-menu/routes/index.ts | 1 + .../routes/virtual-machines-subroutes.ts | 40 + src/app/core/services/index.ts | 1 + .../core/services/snack-bar.service.spec.ts | 44 +- src/app/core/services/snack-bar.service.ts | 21 +- .../alert-dialog/alert-dialog.component.ts | 15 +- .../ask-dialog/ask-dialog.component.ts | 16 +- .../confirm-dialog.component.ts | 14 +- .../dialog/dialog-service/dialog.module.ts | 30 +- .../dialog/dialog-service/dialog.service.ts | 30 +- .../components/event-list.component.html | 12 +- .../events/components/event-list.component.ts | 54 +- .../events/containers/event-list.container.ts | 69 +- src/app/events/event.model.ts | 4 +- src/app/events/event.service.ts | 18 +- src/app/events/events.module.ts | 13 +- src/app/events/redux/events.actions.ts | 13 +- src/app/events/redux/events.effects.ts | 33 +- src/app/events/redux/events.reducers.ts | 138 ++- src/app/home/home.component.html | 11 +- src/app/home/home.component.scss | 20 +- src/app/home/home.component.ts | 47 +- src/app/material/material.module.ts | 4 +- .../charts/aggregation-selector.component.ts | 40 +- src/app/pulse/charts/chart-area.component.ts | 24 +- src/app/pulse/charts/chart.directive.ts | 119 +-- src/app/pulse/charts/pulse-chart.ts | 94 +- .../pulse-cpu-ram-chart.component.ts | 142 +-- .../pulse-disk-chart.component.ts | 106 +-- .../pulse-network-chart.component.ts | 104 +-- src/app/pulse/pulse.module.ts | 24 +- src/app/pulse/pulse.service.ts | 42 +- .../pulse/{unitsUtils.ts => units-utils.ts} | 26 +- src/app/pulse/vm-pulse/vm-pulse.component.ts | 78 +- .../redux/account-tags.actions.ts | 11 +- .../redux/account-tags.effects.spec.ts | 62 +- .../redux/account-tags.effects.ts | 12 +- .../redux/account-tags.reducers.ts | 34 +- .../accounts/redux/accounts.actions.ts | 70 +- .../accounts/redux/accounts.effects.ts | 126 ++- .../accounts/redux/accounts.reducers.ts | 161 ++-- .../redux/affinity-groups.actions.ts | 6 +- .../redux/affinity-groups.effects.ts | 18 +- .../redux/affinity-groups.reducers.ts | 26 +- src/app/reducers/auth/redux/auth.actions.ts | 12 +- src/app/reducers/auth/redux/auth.effects.ts | 13 +- src/app/reducers/auth/redux/auth.reducers.ts | 36 +- .../redux/configurations.actions.ts | 20 +- .../redux/configurations.effects.ts | 48 +- .../redux/configurations.reducers.ts | 42 +- .../redux/disk-offerings.actions.ts | 14 +- .../redux/disk-offerings.effects.spec.ts | 12 +- .../redux/disk-offerings.effects.ts | 13 +- .../redux/disk-offerings.reducers.ts | 75 +- .../reducers/domains/redux/domains.actions.ts | 8 +- .../reducers/domains/redux/domains.effects.ts | 12 +- .../domains/redux/domains.reducers.ts | 45 +- src/app/reducers/index.ts | 34 +- .../redux/resource-counts.actions.ts | 8 +- .../redux/resource-counts.effects.ts | 13 +- .../redux/resource-counts.reducers.ts | 43 +- .../redux/resource-limits.actions.ts | 19 +- .../redux/resource-limits.effects.ts | 30 +- .../redux/resource-limits.reducers.ts | 24 +- src/app/reducers/roles/redux/roles.actions.ts | 8 +- src/app/reducers/roles/redux/roles.effects.ts | 13 +- .../reducers/roles/redux/roles.reducers.ts | 53 +- .../security-groups/redux/sg.actions.ts | 64 +- .../security-groups/redux/sg.effects.ts | 132 +-- .../security-groups/redux/sg.reducers.ts | 196 ++--- .../redux/service-offerings.actions.ts | 14 +- .../redux/service-offerings.effects.ts | 12 +- .../redux/service-offerings.reducers.spec.ts | 106 +-- .../redux/service-offerings.reducers.ts | 91 +- .../snapshots/redux/snapshot.actions.ts | 39 +- .../snapshots/redux/snapshot.effects.spec.ts | 81 +- .../snapshots/redux/snapshot.effects.ts | 201 +++-- .../snapshots/redux/snapshot.reducers.spec.ts | 87 +- .../snapshots/redux/snapshot.reducers.ts | 187 ++-- .../ssh-keys/redux/ssh-key.actions.ts | 39 +- .../ssh-keys/redux/ssh-key.effects.ts | 101 ++- .../ssh-keys/redux/ssh-key.reducers.ts | 119 +-- .../templates/redux/ostype.actions.ts | 7 +- .../templates/redux/ostype.effects.ts | 13 +- .../templates/redux/ostype.reducers.ts | 44 +- .../templates/redux/template.actions.ts | 67 +- .../templates/redux/template.effects.ts | 127 +-- .../templates/redux/template.reducers.ts | 402 ++++----- .../reducers/templates/redux/zone.actions.ts | 6 +- .../reducers/templates/redux/zone.effects.ts | 14 +- .../reducers/templates/redux/zone.reducers.ts | 44 +- .../reducers/vm/redux/vm-creation.effects.ts | 607 +++++++------ src/app/reducers/vm/redux/vm.actions.ts | 275 +++--- src/app/reducers/vm/redux/vm.effects.spec.ts | 331 +++---- src/app/reducers/vm/redux/vm.effects.ts | 815 ++++++++++-------- src/app/reducers/vm/redux/vm.reducers.spec.ts | 12 +- src/app/reducers/vm/redux/vm.reducers.ts | 319 +++---- .../reducers/volumes/redux/volumes.actions.ts | 97 +-- .../volumes/redux/volumes.effects.spec.ts | 245 +++--- .../reducers/volumes/redux/volumes.effects.ts | 339 ++++---- .../volumes/redux/volumes.reducers.ts | 185 ++-- src/app/reducers/zones/redux/zones.actions.ts | 9 +- src/app/reducers/zones/redux/zones.effects.ts | 13 +- .../reducers/zones/redux/zones.reducers.ts | 55 +- .../root-store/config/config-store.module.ts | 8 +- src/app/root-store/config/config.actions.ts | 11 +- src/app/root-store/config/config.effects.ts | 22 +- src/app/root-store/config/config.reducer.ts | 15 +- src/app/root-store/config/config.selectors.ts | 49 +- src/app/root-store/config/index.ts | 4 +- .../idle-monitor/idle-monitor.actions.ts | 5 +- .../idle-monitor/idle-monitor.effects.ts | 27 +- src/app/root-store/index.ts | 11 +- src/app/root-store/layout/index.ts | 9 +- .../root-store/layout/layout-store.module.ts | 9 + src/app/root-store/layout/layout.actions.ts | 17 +- src/app/root-store/layout/layout.reducer.ts | 33 + src/app/root-store/layout/layout.selectors.ts | 11 +- src/app/root-store/meta-reducers.ts | 11 +- .../notifications/notifications.effects.ts | 18 +- src/app/root-store/root-store.module.ts | 24 +- src/app/root-store/router/index.ts | 3 + src/app/root-store/router/router.selectors.ts | 8 + .../root-store/server-data/user-tags/index.ts | 5 +- .../user-tags/user-tags-store.module.ts | 7 +- .../user-tags/user-tags.actions.ts | 179 +--- .../user-tags/user-tags.effects.spec.ts | 13 +- .../user-tags/user-tags.effects.ts | 171 ++-- .../user-tags/user-tags.reducer.ts | 23 +- .../user-tags/user-tags.selectors.ts | 48 +- .../server-data/user-tags/user-tags.state.ts | 36 +- src/app/root-store/state.ts | 2 +- .../sg-rule-addition-form.component.ts | 155 ++-- .../security-group-creation.container.ts | 11 +- .../security-group-details.container.ts | 14 +- .../security-group-page.container.ts | 22 +- .../security-group-sidebar.container.ts | 18 +- .../containers/sg-actions.container.ts | 24 +- .../containers/sg-rules.container.ts | 12 +- .../containers/sg-tags.container.ts | 21 +- src/app/security-group/network-rule.model.ts | 3 +- ...private-security-group-creation.service.ts | 5 +- .../security-group-creation.service.ts | 63 +- .../shared-security-group-creation.service.ts | 1 - ...emplate-security-group-creation.service.ts | 5 +- .../services/network-rule.service.ts | 20 +- .../services/security-group.service.ts | 16 +- .../sg-actions/sg-action.service.ts | 28 +- .../sg-actions.component.ts | 36 +- ...ecurity-group-creation-dialog.component.ts | 31 +- ...oup-creation-security-group.component.html | 2 +- ...group-creation-security-group.component.ts | 5 +- .../security-group-creation.component.ts | 21 +- ...-group-creation-rules-manager.component.ts | 25 +- .../containers/sg-filter.container.html | 4 +- .../containers/sg-filter.container.ts | 44 +- .../sg-filter/sg-filter.component.html | 4 +- .../sg-filter/sg-filter.component.ts | 35 +- .../security-group-card-item.component.ts | 22 +- .../security-group-row-item.component.ts | 22 +- .../security-group-list-item.component.ts | 1 - .../security-group-grouped-list.component.ts | 7 +- .../sg-list/security-group-list.component.ts | 69 +- .../security-group-page.component.html | 4 +- .../sg-page/security-group-page.component.ts | 28 +- .../sg-rules/sg-rule.component.html | 12 +- .../sg-rules/sg-rule.component.ts | 70 +- .../sg-rules/sg-rules-dialog.component.ts | 19 +- .../sg-rules/sg-rules.component.html | 2 +- .../sg-rules/sg-rules.component.spec.ts | 33 +- .../sg-rules/sg-rules.component.ts | 166 ++-- .../security-group-sidebar.component.ts | 17 +- .../security-group-details.component.ts | 11 +- .../sg-sidebar/sg-tags/sg-tags.component.html | 6 +- .../sg-sidebar/sg-tags/sg-tags.component.ts | 15 +- .../security-group-vm-list.component.ts | 14 +- src/app/security-group/sg-view-mode.ts | 2 +- src/app/security-group/sg.model.ts | 17 +- src/app/security-group/sg.module.ts | 16 +- src/app/security-group/sg.routing.ts | 35 +- .../shared/validators/cidr.validator.ts | 4 +- .../shared/validators/icmp-code.validator.ts | 8 +- .../shared/validators/icmp-type.validator.ts | 8 +- .../security-group/shared/validators/index.ts | 2 +- .../shared/validators/ports.validator.ts | 8 +- .../custom-service-offering.component.ts | 20 +- .../service-offering-dialog.component.html | 4 +- .../service-offering-dialog.component.ts | 140 +-- .../service-offering-filter.component.ts | 35 +- .../service-offering-list.component.ts | 118 ++- .../service-offering.module.ts | 21 +- .../api-settings/api-settings.component.ts | 14 +- .../interface-settings.component.ts | 23 +- .../password-update-form.component.ts | 21 +- .../security-settings.component.ts | 15 +- .../session-timeout.component.ts | 8 +- .../theme-selector.component.ts | 19 +- .../vm-settings/vm-settings.component.ts | 9 +- .../containers/settings/settings.component.ts | 49 +- .../settings/selectors/settings.selectors.ts | 8 +- src/app/settings/settings.module.ts | 15 +- .../account-actions.component.ts | 34 +- .../account-actions.service.ts | 20 +- .../account-user-actions.component.ts | 40 +- .../account-user-actions.service.ts | 13 +- .../template-actions/base-template-action.ts | 3 +- .../delete/base-template-delete.ts | 30 +- .../template-actions/delete/iso-delete.ts | 12 +- .../delete/template-delete.ts | 3 +- .../template-actions/iso-actions.service.ts | 1 - .../template-actions.component.ts | 29 +- .../template-actions.container.ts | 27 +- .../template-actions.service.ts | 1 - .../actions/volume-actions/volume-action.ts | 2 +- .../volume-actions.component.ts | 46 +- .../volume-actions.container.ts | 46 +- .../volume-actions/volume-actions.service.ts | 37 +- .../volume-attachment.component.ts | 24 +- .../volume-attachment.container.ts | 20 +- .../volume-delete-dialog.component.ts | 6 +- .../volume-actions/volume-resize.container.ts | 17 +- .../volume-resize.component.html | 2 +- .../volume-resize.component.spec.ts | 72 +- .../volume-resize/volume-resize.component.ts | 44 +- .../character-count.component.ts | 9 +- .../clipboard-button.component.ts | 11 +- .../color-picker.component.spec.ts | 30 +- .../color-picker/color-picker.component.ts | 47 +- .../create-update-delete-dialog.component.ts | 93 +- .../date-picker/calendar-month.component.ts | 37 +- .../date-picker/calendar-year.component.html | 6 +- .../date-picker/calendar-year.component.ts | 46 +- .../date-picker/calendar.component.html | 6 +- .../date-picker/calendar.component.ts | 47 +- .../date-picker/date-display.component.ts | 27 +- .../date-picker-dialog.component.html | 2 +- .../date-picker-dialog.component.ts | 5 +- .../date-picker/date-picker.component.ts | 85 +- .../components/date-picker/dateUtils.ts | 64 +- .../day-period/day-period.component.ts | 18 +- .../description/description.component.ts | 16 +- .../disk-offering-dialog.component.html | 2 +- .../disk-offering-dialog.component.spec.ts | 79 +- .../disk-offering-selector.component.ts | 47 +- .../divider-vertical.component.ts | 5 +- .../draggable-select.component.ts | 36 +- .../draggable-select.module.ts | 12 +- .../shared/components/fab/fab.component.ts | 13 +- .../fancy-select/fancy-select.component.ts | 2 +- .../grouped-list/grouped-list.component.ts | 38 +- .../abstract-inline-edit.component.ts | 13 +- .../inline-edit/inline-edit.component.ts | 26 +- .../input-group/input-group.component.ts | 3 +- .../keyboards/keyboards.component.ts | 18 +- .../components/list/list.component.html | 2 +- .../shared/components/list/list.component.ts | 12 +- .../shared/components/list/list.service.ts | 27 +- .../components/loader/loader.component.ts | 28 +- .../no-results/no-results.component.ts | 6 +- .../notification-box-item.component.ts | 5 +- .../notification-box.component.html | 4 +- .../notification-box.component.ts | 22 +- .../overlay-loading.component.ts | 4 +- .../parameters-edit-pair.component.ts | 21 +- .../parameters-pair.component.html | 2 +- .../parameters-pair.component.ts | 20 +- src/app/shared/components/popover/index.ts | 2 +- .../popover/popover-trigger.directive.spec.ts | 88 +- .../popover/popover-trigger.directive.ts | 92 +- .../components/popover/popover.component.ts | 5 +- .../animated-slash.component.ts | 3 +- .../progress-logger-message.component.ts | 21 +- .../progress-logger-message.ts | 6 +- .../progress-logger.service.ts | 32 - .../progress-logger.component.ts | 6 +- .../components/reload/reload.component.ts | 3 +- .../components/search/search.component.ts | 12 +- ...security-group-builder-rule.component.html | 2 +- ...urity-group-builder-rule.component.spec.ts | 13 +- .../security-group-builder-rule.component.ts | 70 +- .../security-group-builder/rules.ts | 30 +- .../security-group-builder.component.html | 4 +- .../security-group-builder.component.spec.ts | 100 ++- .../security-group-builder.component.ts | 77 +- ...-group-manager-base-templates.component.ts | 13 +- .../sidebar-container.component.ts | 25 +- .../components/sidebar/sidebar.component.ts | 31 +- .../components/slider/slider.component.ts | 35 +- .../round-state-indicator.component.ts | 5 +- .../square-state-indicator.component.ts | 5 +- .../state-indicator/state-indicator.ts | 3 +- .../time-picker/time-picker.component.ts | 38 +- .../time-zone/time-zone.component.ts | 29 +- .../components/time-zone/time-zone.service.ts | 8 +- .../components/top-bar/top-bar.component.html | 7 - .../components/top-bar/top-bar.component.scss | 11 - .../components/top-bar/top-bar.component.ts | 22 +- .../view-mode-switch.component.ts | 25 +- .../vm-statistics/vm-statistic.container.ts | 18 +- .../vm-statistics/vm-statistics.component.ts | 95 +- .../decorators/backend-resource.decorator.ts | 7 +- .../decorators/field-mapper.decorator.ts | 11 - src/app/shared/decorators/index.ts | 1 - .../shared/decorators/zone-name.decorator.ts | 5 +- .../directives/badge/badge.directive.ts | 6 +- .../directives/forbidden-values-validator.ts | 9 +- .../directives/forbidden-values.directive.ts | 10 +- .../input-type-number.directive.spec.ts | 16 +- .../directives/input-type-number.directive.ts | 40 +- .../shared/directives/integer-validator.ts | 4 +- .../directives/integer-value.directive.ts | 9 +- .../shared/directives/loading.directive.ts | 20 +- src/app/shared/icmp/icmp-types.ts | 16 +- .../interfaces/action-service.interface.ts | 7 +- src/app/shared/interfaces/action.interface.ts | 7 +- src/app/shared/interfaces/filter-component.ts | 1 - src/app/shared/interfaces/index.ts | 2 +- .../interfaces/ngrx-entities.interface.ts | 2 +- .../shared/interfaces/taggable.interface.ts | 4 +- src/app/shared/models/account-user.model.ts | 4 +- src/app/shared/models/account.model.ts | 12 +- src/app/shared/models/affinity-group.model.ts | 9 +- src/app/shared/models/async-job.model.spec.ts | 3 +- src/app/shared/models/async-job.model.ts | 11 +- src/app/shared/models/base.model.spec.ts | 55 -- src/app/shared/models/base.model.ts | 67 +- src/app/shared/models/color.model.ts | 6 +- .../compute-offering-class.interface.ts | 2 +- .../shared/models/config/config.interface.ts | 17 +- ...m-compute-offering-parameters.interface.ts | 1 - .../shared/models/config/image-group.model.ts | 2 +- src/app/shared/models/config/index.ts | 3 +- ...offering-compatibility-policy.interface.ts | 6 +- .../security-group-template.interface.ts | 10 + ...service-offering-availability.interface.ts | 8 +- src/app/shared/models/configuration.model.ts | 4 +- src/app/shared/models/domain.model.ts | 8 +- src/app/shared/models/grouping.model.ts | 10 +- src/app/shared/models/hypervisor.model.ts | 4 +- src/app/shared/models/instance-group.model.ts | 2 +- src/app/shared/models/nic.model.ts | 7 +- src/app/shared/models/offering.model.ts | 10 +- src/app/shared/models/os-type.model.ts | 6 +- src/app/shared/models/resource-count.model.ts | 10 +- src/app/shared/models/resource-limit.model.ts | 6 +- src/app/shared/models/role.model.ts | 5 +- .../shared/models/service-offering.model.ts | 8 +- src/app/shared/models/snapshot.model.ts | 19 +- src/app/shared/models/ssh-keypair.model.ts | 4 +- src/app/shared/models/tag.model.ts | 17 +- src/app/shared/models/user.model.ts | 8 +- src/app/shared/models/volume.model.ts | 27 +- src/app/shared/models/zone.model.ts | 4 +- src/app/shared/pipes/division.pipe.ts | 5 +- src/app/shared/pipes/highlight.pipe.ts | 2 +- src/app/shared/pipes/index.ts | 4 +- ...ifyDate.pipe.ts => stringify-date.pipe.ts} | 3 +- ...ifyTime.pipe.ts => stringify-time.pipe.ts} | 3 +- src/app/shared/pipes/view-value.pipe.ts | 8 +- src/app/shared/pipes/volume-sort.pipe.ts | 5 +- src/app/shared/services/account.service.ts | 22 +- .../shared/services/affinity-group.service.ts | 30 +- .../shared/services/async-job.service.spec.ts | 78 +- src/app/shared/services/async-job.service.ts | 64 +- src/app/shared/services/auth-guard.service.ts | 26 +- src/app/shared/services/auth.service.spec.ts | 114 ++- src/app/shared/services/auth.service.ts | 40 +- .../services/base-backend-cached.service.ts | 21 +- .../services/base-backend.service.spec.ts | 127 ++- .../shared/services/base-backend.service.ts | 142 ++- .../shared/services/base-http-interceptor.ts | 54 +- src/app/shared/services/cache.service.ts | 2 - src/app/shared/services/cache.ts | 9 +- .../shared/services/configuration.service.ts | 6 +- .../services/date-time-formatter.service.ts | 18 +- .../shared/services/disk-offering.service.ts | 32 +- src/app/shared/services/domain.service.ts | 2 +- src/app/shared/services/error.service.spec.ts | 10 +- src/app/shared/services/error.service.ts | 33 +- .../shared/services/filter.service.spec.ts | 596 ++++++------- src/app/shared/services/filter.service.ts | 36 +- src/app/shared/services/hypervisor.service.ts | 7 +- .../shared/services/instance-group.service.ts | 5 +- .../jobs-notification.service.spec.ts | 114 +-- .../services/jobs-notification.service.ts | 21 +- .../shared/services/local-storage.service.ts | 16 +- .../shared/services/login-guard.service.ts | 21 +- src/app/shared/services/offering.service.ts | 40 +- src/app/shared/services/os-type.service.ts | 20 +- .../shared/services/resource-count.service.ts | 11 +- .../shared/services/resource-limit.service.ts | 5 +- .../shared/services/resource-usage.service.ts | 32 +- src/app/shared/services/role.service.ts | 2 +- .../shared/services/router-utils.service.ts | 15 +- .../services/service-offering.service.spec.ts | 54 +- .../services/service-offering.service.ts | 8 +- .../services/session-storage.service.ts | 17 +- src/app/shared/services/snapshot.service.ts | 37 +- .../shared/services/ssh-keypair.service.ts | 20 +- .../shared/services/storage.service.spec.ts | 7 +- src/app/shared/services/storage.service.ts | 6 +- src/app/shared/services/style.service.ts | 33 +- .../shared/services/tags/account-tag-keys.ts | 4 +- .../services/tags/account-tag.service.spec.ts | 74 +- .../services/tags/account-tag.service.ts | 54 +- .../services/tags/description-tag.service.ts | 37 +- .../tags/entity-tag-service.interface.ts | 2 +- .../services/tags/mark-for-removal.service.ts | 23 - .../services/tags/security-group-tag-keys.ts | 4 +- .../tags/security-group-tag.service.ts | 47 +- .../shared/services/tags/snapshot-tag-keys.ts | 4 +- .../services/tags/snapshot-tag.service.ts | 28 +- src/app/shared/services/tags/tag.service.ts | 59 +- .../shared/services/tags/template-tag-keys.ts | 4 +- .../services/tags/template-tag.service.ts | 21 +- src/app/shared/services/tags/vm-tag-keys.ts | 2 +- .../shared/services/tags/vm-tag.service.ts | 98 +-- .../shared/services/tags/volume-tag-keys.ts | 2 +- .../services/tags/volume-tag.service.ts | 26 +- src/app/shared/services/user.service.ts | 8 +- .../services/utils/utils.service.spec.ts | 62 +- .../shared/services/utils/utils.service.ts | 11 +- src/app/shared/services/volume.service.ts | 78 +- src/app/shared/services/zone.service.ts | 1 - src/app/shared/shared.module.ts | 33 +- src/app/shared/types/day-of-week.ts | 2 +- src/app/shared/types/keyboard-layout.ts | 2 +- src/app/shared/types/language.ts | 2 +- src/app/shared/types/time-format.ts | 2 +- src/app/shared/utils/cidr-utils.spec.ts | 11 +- src/app/shared/utils/cidr-utils.ts | 24 +- src/app/shared/utils/filter.ts | 2 +- .../utils/{isUrl.spec.ts => is-url.spec.ts} | 26 +- src/app/shared/utils/{isUrl.ts => is-url.ts} | 5 +- .../utils/{padStart.ts => pad-start.ts} | 0 src/app/shared/utils/reorder-groupings.ts | 7 +- .../utils/subnet-mask-to-cidr-suffix.spec.ts | 14 +- .../utils/subnet-mask-to-cidr-suffix.ts | 8 +- .../validators/directives/url.directive.ts | 15 +- src/app/shared/validators/url.validator.ts | 8 +- .../day-of-week/day-of-week.component.ts | 38 +- .../daily/daily-policy.component.ts | 24 +- .../hourly/hourly-policy.component.ts | 26 +- .../monthly/monthly-policy.component.ts | 20 +- .../policy-editor.component.html | 8 +- .../policy-editor/policy-editor.component.ts | 22 +- .../weekly/weekly-policy.component.ts | 18 +- .../policy-list/policy-list.component.ts | 51 +- .../policy-view-builder.service.ts | 31 +- .../recurring-snapshots.component.html | 12 +- .../recurring-snapshots.component.ts | 68 +- .../snapshot-policy-type.ts | 2 +- .../snapshot-policy.model.ts | 5 +- .../snapshot-policy.service.ts | 60 +- .../stored-number/stored-number.component.ts | 18 +- src/app/snapshot/snapshot.module.ts | 28 +- src/app/snapshot/snapshot.routing.ts | 9 +- .../create-volume/create-volume.component.ts | 8 +- .../create-volume/create-volume.container.ts | 21 +- .../snapshot-filter.component.html | 4 +- .../snapshot-filter.component.ts | 59 +- .../snapshot-filter.container.ts | 160 ++-- .../snapshot-action.component.ts | 27 +- .../snapshot-action.container.ts | 19 +- .../snapshot-action.service.ts | 68 +- .../snapshot-card-item.component.html | 2 +- .../snapshot-card-item.component.ts | 27 +- .../snapshot-item.component.ts | 29 +- .../snapshot-list-item.component.html | 2 +- .../snapshot-list-item.component.ts | 27 +- .../snapshot-sidebar.component.html | 2 +- .../snapshot-sidebar.component.ts | 34 +- .../snapshot-sidebar.container.ts | 16 +- .../snapshots-page.component.html | 2 +- .../snapshots-page.component.ts | 31 +- .../snapshots-page.container.ts | 22 +- .../ssh-key-filter.container.ts | 43 +- .../ssh-key-page/ssh-key-page.container.ts | 30 +- .../ssh-key-creation-dialog.container.ts | 12 +- .../ssh-key-creation-dialog.component.ts | 12 +- .../ssh-key-creation.component.ts | 28 +- .../ssh-private-key-dialog.component.ts | 7 +- .../ssh-key-filter.component.html | 4 +- .../ssh-key-filter.component.ts | 20 +- .../ssh-key-fingerprint.component.ts | 7 +- .../card-item/ssh-key-card-item.component.ts | 25 +- .../row-item/ssh-key-row-item.component.ts | 25 +- .../ssh-key-item.component.ts | 1 - .../ssh-key-list/ssh-key-list.component.ts | 31 +- .../ssh-key-sidebar.component.ts | 25 +- .../ssh-keys-page.component.html | 6 +- .../ssh-keys-page/ssh-keys-page.component.ts | 52 +- src/app/ssh-keys/ssh-keys.module.ts | 10 +- src/app/ssh-keys/ssh-keys.routing.ts | 11 +- .../tag-category/tag-category.component.html | 4 +- .../tag-category/tag-category.component.ts | 38 +- src/app/tags/tag-edit/tag-edit.component.ts | 20 +- src/app/tags/tag-keys/user-tag-keys.ts | 2 - src/app/tags/tag/tag.component.ts | 41 +- .../tags/tags-view/tags-view.component.html | 6 +- src/app/tags/tags-view/tags-view.component.ts | 131 +-- src/app/tags/tags.component.ts | 75 +- src/app/tags/tags.module.ts | 26 +- ...so-attachment-filter-selector.container.ts | 54 +- .../containers/template-filter.container.ts | 113 +-- .../containers/template-page.container.html | 2 +- .../containers/template-page.container.ts | 33 +- .../iso-attachment.component.ts | 3 +- .../template/shared/base-template.model.ts | 17 +- .../template/shared/base-template.service.ts | 146 ++-- src/app/template/shared/iso.model.ts | 1 - src/app/template/shared/iso.service.ts | 10 +- src/app/template/shared/template.model.ts | 1 - .../template/shared/template.service.spec.ts | 185 ++-- src/app/template/shared/template.service.ts | 20 +- .../containers/template-creation.container.ts | 23 +- .../template-creation-dialog.component.ts | 17 +- .../template-creation.component.spec.ts | 53 +- .../template-creation.component.ts | 57 +- .../template-filter-list.container.ts | 5 +- ...mplate-filter-list-selector.component.html | 8 +- ...template-filter-list-selector.component.ts | 72 +- .../template-filter-list.component.ts | 27 +- .../template-filters.component.ts | 117 ++- .../template-card-list.component.ts | 26 +- .../template-list/template-list.component.ts | 29 +- .../template-page.component.html | 6 +- .../template-page/template-page.component.ts | 35 +- .../base-template-sidebar.component.ts | 12 +- .../base-template-sidebar.container.ts | 17 +- .../containers/details.container.ts | 10 +- .../containers/iso-zones.container.ts | 9 +- .../containers/tags.container.ts | 35 +- .../containers/template-zones.container.ts | 9 +- .../details/details.component.html | 2 +- .../details/details.component.ts | 9 +- .../details/iso-details.component.ts | 5 +- .../details/template-details.component.ts | 5 +- .../template-sidebar/iso-sidebar.component.ts | 5 +- .../template-actions-sidebar.container.ts | 12 +- .../template-actions-sidebar.component.ts | 9 +- .../template-description.component.ts | 6 +- .../template-group-selector.container.ts | 9 +- .../containers/template-group.container.ts | 5 +- .../template-group-selector.component.html | 6 +- .../template-group-selector.component.ts | 42 +- .../template-group.component.ts | 34 +- .../template-os-icon.component.ts | 9 +- .../template-os-icon.container.ts | 17 +- .../template-os/template-os.component.ts | 9 +- .../template-os/template-os.container.ts | 17 +- .../template-sidebar.component.ts | 5 +- .../zones/iso-zones.component.ts | 2 +- .../zones/template-zones.component.ts | 2 +- .../template-sidebar/zones/zones.component.ts | 16 +- .../template-tags.component.html | 6 +- .../template-tags/template-tags.component.ts | 32 +- src/app/template/template.module.ts | 16 +- src/app/template/template.routing.ts | 50 +- .../card-item/template-card-item.component.ts | 23 +- .../row-item/template-row-item.component.ts | 36 +- .../template/template/template.component.ts | 6 +- .../custom-query-encoder.spec.ts | 1 - .../custom-query-encoder.ts | 1 - src/app/utils/mixins/contructor.ts | 5 +- src/app/utils/mixins/with-unsubscribe.ts | 6 +- .../vm/container/network-detail.container.ts | 36 +- .../service-offering-dialog.container.ts | 44 +- .../vm/container/storage-detail.container.ts | 94 +- src/app/vm/container/vm-actions.container.ts | 50 +- src/app/vm/container/vm-detail.container.ts | 49 +- src/app/vm/container/vm-filter.container.ts | 104 +-- src/app/vm/container/vm-sidebar.container.ts | 13 +- src/app/vm/container/vm-tags.container.ts | 45 +- .../container/vm-volume-details.container.ts | 15 +- src/app/vm/container/vm.container.ts | 42 +- src/app/vm/selectors/index.ts | 4 +- .../service-offering.selectors.spec.ts | 327 ++++--- .../selectors/service-offering.selectors.ts | 90 +- ...mpute-offering-view-model.selector.spec.ts | 211 +++-- .../compute-offering-view-model.selector.ts | 214 +++-- src/app/vm/selectors/view-models/index.ts | 2 +- src/app/vm/services/access.service.ts | 5 +- src/app/vm/services/http-access.service.ts | 29 +- src/app/vm/services/ssh-access.service.ts | 22 +- src/app/vm/services/vnc-access.service.ts | 6 +- src/app/vm/shared/vm-actions.service.ts | 83 +- .../vm/shared/vm-compatibility-policy.spec.ts | 22 +- src/app/vm/shared/vm-compatibility-policy.ts | 21 +- .../vm-destroy-dialog.component.spec.ts | 41 +- .../vm-destroy-dialog.component.ts | 6 +- .../vm-password/vm-password.component.ts | 48 +- src/app/vm/shared/vm.model.ts | 157 +--- src/app/vm/shared/vm.service.ts | 101 +-- src/app/vm/vm-actions/vm-action.ts | 2 +- .../vm-access.component.html | 4 +- .../vm-access.component.ts | 22 +- .../vm-actions.component.ts | 68 +- .../vm-password-dialog.component.ts | 12 +- ...-group-manager-existing-group.component.ts | 5 +- ...rity-group-rules-manager.component.spec.ts | 53 +- ...-security-group-rules-manager.component.ts | 23 +- .../security-group-selector.component.ts | 27 +- .../vm-creation-security-group.container.ts | 17 +- .../vm-creation-security-group.component.html | 6 +- .../vm-creation-security-group.component.ts | 17 +- ...ervice-offering-selector.component.spec.ts | 81 +- .../service-offering-selector.component.ts | 80 +- .../containers/vm-creation.container.ts | 33 +- .../vm/vm-creation/data/vm-creation-state.ts | 11 +- .../postdeployment.component.html | 2 +- .../postdeployment.component.ts | 40 +- .../vm-creation-security-group-data.ts | 26 +- .../vm-creation-security-group-mode.ts | 2 +- .../vm-creation-service-offering.container.ts | 24 +- ...vm-creation-security-group.service.spec.ts | 8 +- .../vm-creation-security-group.service.ts | 26 +- .../ssh-key-selector.component.ts | 15 +- .../vm-creation-agreement.component.ts | 29 +- .../installation-source-dialog.component.ts | 8 +- .../vm-creation-template.container.ts | 21 +- .../vm-creation-template.component.spec.ts | 43 +- .../vm-creation-template.component.ts | 57 +- .../vm-template-dialog.component.html | 8 +- .../template/vm-template-dialog.component.ts | 67 +- .../vm-creation-dialog.component.ts | 17 +- .../vm/vm-creation/vm-creation.component.html | 10 +- .../vm/vm-creation/vm-creation.component.ts | 136 +-- src/app/vm/vm-filter/vm-filter.component.html | 12 +- src/app/vm/vm-filter/vm-filter.component.ts | 53 +- .../vm-list-card-item.component.html | 2 +- .../card-item/vm-list-card-item.component.ts | 26 +- .../row-item/vm-list-row-item.component.html | 2 +- .../row-item/vm-list-row-item.component.ts | 26 +- .../vm/vm-list-item/vm-list-item.component.ts | 48 +- src/app/vm/vm-list/vm-list.component.ts | 25 +- src/app/vm/vm-page/vm-page.component.html | 4 +- src/app/vm/vm-page/vm-page.component.ts | 66 +- .../affinity-group-selector.component.html | 8 +- .../affinity-group-selector.component.ts | 20 +- .../vm-sidebar/color/vm-color.component.html | 2 +- .../vm/vm-sidebar/color/vm-color.component.ts | 53 +- .../instance-group-selector.component.html | 8 +- .../instance-group-selector.component.ts | 25 +- .../firewall-rules-detail.component.html | 9 +- .../firewall-rules-detail.component.ts | 29 +- .../firewall-rules-detail.container.ts | 10 +- .../nics/nic-list/nic-list.component.html | 4 +- .../nics/nic-list/nic-list.component.ts | 19 +- .../nic/nic-fields/nic-fields.component.ts | 6 +- .../nics/nic/nic.component.html | 4 +- .../network-detail/nics/nic/nic.component.ts | 20 +- .../secondary-ip-list.component.html | 2 +- .../secondary-ip-list.component.ts | 39 +- .../secondary-ip/secondary-ip.component.ts | 11 +- .../ssh-keypair-reset.component.ts | 6 +- .../statistics/statistics.component.html | 14 +- .../statistics/statistics.component.ts | 11 +- .../storage-detail/iso/iso.component.ts | 23 +- .../volume-attachment-detail.component.ts | 30 +- .../volume-attachment-dialog.component.ts | 5 +- .../snapshot-creation.component.ts | 22 +- .../snapshot/snapshot-modal.component.ts | 33 +- .../snapshot/snapshot-modal.container.ts | 27 +- .../volumes/snapshot/snapshots.component.html | 2 +- .../volumes/snapshot/snapshots.component.ts | 47 +- .../volumes/snapshot/snapshots.container.ts | 25 +- .../volume-details.component.ts | 24 +- .../volumes/volume/volume.component.ts | 16 +- .../volumes/volumes.component.html | 2 +- .../volumes/volumes.component.ts | 10 +- .../affinity-group.component.html | 4 +- .../affinity-group.component.ts | 28 +- .../instance-group.component.html | 2 +- .../instance-group.component.ts | 33 +- .../service-offering-details.component.html | 2 +- .../service-offering-details.component.ts | 23 +- .../vm-detail/ssh/ssh-keypair.component.html | 6 +- .../vm-detail/ssh/ssh-keypair.component.ts | 33 +- .../vm-detail-template.component.html | 2 +- .../template/vm-detail-template.component.ts | 11 +- .../vm-detail/zone/zone.component.html | 2 +- .../vm-detail/zone/zone.component.ts | 11 +- .../vm/vm-sidebar/vm-sidebar.component.html | 4 +- src/app/vm/vm-sidebar/vm-sidebar.component.ts | 10 +- .../vm-sidebar/vm-tags/vm-tags.component.html | 6 +- .../vm-sidebar/vm-tags/vm-tags.component.ts | 17 +- src/app/vm/vm.module.ts | 15 +- src/app/vm/vm.routing.ts | 34 +- .../container/volume-creation.container.ts | 42 +- .../container/volume-details.container.ts | 36 +- .../container/volume-filter.container.ts | 115 +-- .../container/volume-sidebar.container.ts | 20 +- .../volume-snapshot-details.container.ts | 13 +- src/app/volume/container/volume.container.ts | 36 +- .../volume-creation-dialog.component.html | 2 +- .../volume-creation-dialog.component.ts | 28 +- .../volume-creation.component.ts | 19 +- .../volume-filter.component.html | 12 +- .../volume-filter/volume-filter.component.ts | 53 +- src/app/volume/volume-item.ts | 22 +- .../card-item/volume-card-item.component.ts | 21 +- .../row-item/volume-row-item.component.ts | 29 +- .../volume-item/volume-item.component.ts | 9 +- .../volume-grouped-list.component.ts | 2 +- .../volume-list/volume-list.component.ts | 22 +- .../volume-page/volume-page.component.html | 4 +- .../volume-page/volume-page.component.ts | 87 +- .../volume-actions-sidebar.component.ts | 6 +- ...olume-sidebar-disk-offering.component.html | 2 +- .../volume-sidebar-disk-offering.component.ts | 7 +- .../volume-sidebar-volume.component.html | 2 +- .../volume/volume-sidebar-volume.component.ts | 15 +- .../volume-snapshot-creation.component.ts | 11 +- .../snapshot/volume-snapshot.component.html | 2 +- .../snapshot/volume-snapshot.component.ts | 14 +- .../volume-snapshot-details.component.html | 2 +- .../volume-snapshot-details.component.ts | 13 +- .../volume-sidebar.component.ts | 9 +- src/app/volume/volume.module.ts | 17 +- src/app/volume/volume.routing.ts | 21 +- src/config/config-example.json | 32 +- src/environments/environment.prod.ts | 2 +- src/environments/environment.ts | 2 +- src/i18n/ru.json | 2 +- src/karma.conf.js | 29 +- src/main.ts | 1 + src/polyfills.ts | 1 - src/style/__variables.scss | 2 +- src/style/_app-theme.scss | 16 +- src/test.ts | 10 +- src/testutils/data/accounts.ts | 4 +- src/testutils/data/compute-offerings.ts | 2 - src/testutils/data/vitrual-machines.ts | 63 +- src/testutils/mocks/mock-snack-bar.service.ts | 5 +- .../mocks/mock-translate.pipe.spec.ts | 2 +- .../mocks/mock-translate.service.spec.ts | 17 +- .../mocks/model-services/entity-data.spec.ts | 43 +- .../mock-affinity-group.service.spec.ts | 5 +- ...ck-custom-service-offering.service.spec.ts | 13 +- .../mock-disk-offering.service.spec.ts | 5 +- .../mock-disk-storage.service.spec.ts | 1 - .../services/mock-iso.service.spec.ts | 5 +- .../mock-resource-usage.service.spec.ts | 13 +- .../mock-security-group.service.spec.ts | 4 +- .../mock-service-offering.service.spec.ts | 8 +- .../services/mock-snapshots.service.spec.ts | 4 +- .../services/mock-ssh.service.spec.ts | 5 +- .../services/mock-template.service.spec.ts | 9 +- .../services/mock-vm.service.spec.ts | 1 - .../services/mock-zone.service.spec.ts | 5 +- .../mock-security-group-tag.service.ts | 1 - .../tag-services/mock-snapshot-tag.service.ts | 1 - .../mocks/tag-services/mock-tag.service.ts | 5 +- .../tag-services/mock-template-tag.service.ts | 1 - .../tag-services/mock-user-tag.service.ts | 11 - .../mocks/tag-services/mock-vm-tag.service.ts | 1 - .../tag-services/mock-volume-tag.service.ts | 1 - src/testutils/ngrx-test-store.ts | 3 +- src/tslint.json | 15 +- tslint.json | 113 +-- version | 1 + yarn.lock | 269 +++++- 859 files changed, 13662 insertions(+), 14482 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc delete mode 100644 src/app/core/components/index.ts delete mode 100644 src/app/core/components/sidenav/_sidenav-theme.scss delete mode 100644 src/app/core/components/sidenav/sidenav-animations.ts delete mode 100644 src/app/core/components/sidenav/sidenav-routes.ts delete mode 100644 src/app/core/components/sidenav/sidenav.component.html delete mode 100644 src/app/core/components/sidenav/sidenav.component.scss delete mode 100644 src/app/core/components/sidenav/sidenav.component.ts create mode 100644 src/app/core/config/validation-schemes/api-doc-link.scheme.json delete mode 100644 src/app/core/config/validation-schemes/configure-sidenav.scheme.json create mode 100644 src/app/core/config/validation-schemes/offering-compatibility-policy.scheme.json create mode 100644 src/app/core/config/validation-schemes/security-group-templates.scheme.json create mode 100644 src/app/core/config/validation-schemes/vm-colors.scheme.json create mode 100644 src/app/core/nav-menu/components/app-nav/app-nav.component.html create mode 100644 src/app/core/nav-menu/components/app-nav/app-nav.component.scss create mode 100644 src/app/core/nav-menu/components/app-nav/app-nav.component.ts create mode 100644 src/app/core/nav-menu/components/index.ts create mode 100644 src/app/core/nav-menu/components/license/license.component.html create mode 100644 src/app/core/nav-menu/components/license/license.component.scss create mode 100644 src/app/core/nav-menu/components/license/license.component.ts create mode 100644 src/app/core/nav-menu/components/menu-header/menu-header.component.html create mode 100644 src/app/core/nav-menu/components/menu-header/menu-header.component.scss create mode 100644 src/app/core/nav-menu/components/menu-header/menu-header.component.ts create mode 100644 src/app/core/nav-menu/components/section-nav/section-nav.component.html create mode 100644 src/app/core/nav-menu/components/section-nav/section-nav.component.scss create mode 100644 src/app/core/nav-menu/components/section-nav/section-nav.component.ts create mode 100644 src/app/core/nav-menu/models/index.ts create mode 100644 src/app/core/nav-menu/models/route.interface.ts create mode 100644 src/app/core/nav-menu/models/subroute.interface.ts create mode 100644 src/app/core/nav-menu/nav-menu-theme.scss create mode 100644 src/app/core/nav-menu/nav-menu.service.spec.ts create mode 100644 src/app/core/nav-menu/nav-menu.service.ts create mode 100644 src/app/core/nav-menu/routes/accounts-subroutes.ts create mode 100644 src/app/core/nav-menu/routes/app-nav-routes.ts create mode 100644 src/app/core/nav-menu/routes/index.ts create mode 100644 src/app/core/nav-menu/routes/virtual-machines-subroutes.ts rename src/app/pulse/{unitsUtils.ts => units-utils.ts} (58%) create mode 100644 src/app/root-store/layout/layout-store.module.ts create mode 100644 src/app/root-store/layout/layout.reducer.ts create mode 100644 src/app/root-store/router/index.ts create mode 100644 src/app/root-store/router/router.selectors.ts delete mode 100644 src/app/shared/components/progress-logger/progress-logger.service.ts delete mode 100644 src/app/shared/decorators/field-mapper.decorator.ts delete mode 100644 src/app/shared/models/base.model.spec.ts create mode 100644 src/app/shared/models/config/security-group-template.interface.ts rename src/app/shared/pipes/{stringifyDate.pipe.ts => stringify-date.pipe.ts} (93%) rename src/app/shared/pipes/{stringifyTime.pipe.ts => stringify-time.pipe.ts} (93%) delete mode 100644 src/app/shared/services/tags/mark-for-removal.service.ts rename src/app/shared/utils/{isUrl.spec.ts => is-url.spec.ts} (88%) rename src/app/shared/utils/{isUrl.ts => is-url.ts} (98%) rename src/app/shared/utils/{padStart.ts => pad-start.ts} (100%) create mode 100644 version diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..36244fb933 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +package.json +reports diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..5e2863a11f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "printWidth": 100, + "singleQuote": true, + "trailingComma": "all" +} diff --git a/README.md b/README.md index 181d551176..6da948a686 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,40 @@ [![Build Status](https://travis-ci.org/bwsw/cloudstack-ui.svg?branch=master)](https://travis-ci.org/bwsw/cloudstack-ui) [![Coverage Status](https://coveralls.io/repos/github/bwsw/cloudstack-ui/badge.svg?branch=master)](https://coveralls.io/github/bwsw/cloudstack-ui?branch=master) -Table of Contents -================= - - * [CloudStack-UI](#cloudstack-ui) - * [Project Story](#project-story) - * [Implementation Details](#implementation-details) - * [Features Supported](#features-supported) - * [Plugins Supported](#plugins-supported) - * [Features Yet Unsupported](#features-yet-unsupported) - * [Current To Do's](#current-to-dos) - * [Long Term To Do's](#long-term-to-dos) - * [Far Away To Do's](#far-away-to-dos) - * [Screenshots and Features descriptions](#screenshots--features-descriptions) - * [Login view](#login-view) - * [Virtual machines](#virtual-machines-view) - * [New virtual machine form](#new-virtual-machine-form) - * [Resource usage bar](#resource-usage-bar) - * [Storage](#storage) - * [Images](#images) - * [Snapshots](#snapshots) - * [Firewall](#firewall) - * [Activity log](#activity-log) - * [Accounts](#accounts) - * [Documentation](#documentation) - * [User Guide](#user-guide) - * [Getting started guide](#getting-started-guide) - * [Prerequisites](#prerequisites) - * [Download and install dependencies](#download-and-install-dependencies) - * [Main commands](#main-commands) - * [Deployment](#deployment) - * [Main UI Container](#main-ui-container) - * [Assisting object cleanup container](#assisting-object-cleanup-container) - * [Versioning](#versioning) - * [Configuration Options](#configuration-options) - * [Project Sponsors](#project-sponsors) - * [How to Contribute](#how-to-contribute) - * [License](#license) +# Table of Contents + +- [CloudStack-UI](#cloudstack-ui) + - [Project Story](#project-story) + - [Implementation Details](#implementation-details) + - [Features Supported](#features-supported) + - [Plugins Supported](#plugins-supported) + - [Features Yet Unsupported](#features-yet-unsupported) + - [Current To Do's](#current-to-dos) + - [Long Term To Do's](#long-term-to-dos) + - [Far Away To Do's](#far-away-to-dos) + - [Screenshots and Features descriptions](#screenshots--features-descriptions) + - [Login view](#login-view) + - [Virtual machines](#virtual-machines-view) + - [New virtual machine form](#new-virtual-machine-form) + - [Resource usage bar](#resource-usage-bar) + - [Storage](#storage) + - [Images](#images) + - [Snapshots](#snapshots) + - [Firewall](#firewall) + - [Activity log](#activity-log) + - [Accounts](#accounts) +- [Documentation](#documentation) + - [User Guide](#user-guide) + - [Getting started guide](#getting-started-guide) + - [Prerequisites](#prerequisites) + - [Download and install dependencies](#download-and-install-dependencies) + - [Main commands](#main-commands) + - [Deployment](#deployment) + - [Main UI Container](#main-ui-container) + - [Versioning](#versioning) + - [Configuration Options](#configuration-options) +- [Project Sponsors](#project-sponsors) + - [How to Contribute](#how-to-contribute) + - [License](#license) # CloudStack-UI @@ -52,14 +50,14 @@ The project is developed by Bitworks Software Frontend Division within the educa ## Implementation Details -* Designed compatible with [Apache CloudStack](http://cloudstack.apache.org/) 4.11 and has been tested for the previous versions of CS (4.9, 4.10). -* Powered by [Angular](https://angular.io/) and [Material 2](https://material.angular.io/). -* Tested and works fine in the next modern browsers: - * Google Chrome 60.0.3112.78 - * Chromium 60.0.3169.0 - * Mozilla Firefox 54.0.1 - * Safari 5.1.7 - * Internet Explorer 11.483.150630 +- Designed compatible with [Apache CloudStack](http://cloudstack.apache.org/) 4.11 and has been tested for the previous versions of CS (4.9, 4.10). +- Powered by [Angular](https://angular.io/) and [Material 2](https://material.angular.io/). +- Tested and works fine in the next modern browsers: + - Google Chrome 60.0.3112.78 + - Chromium 60.0.3169.0 + - Mozilla Firefox 54.0.1 + - Safari 5.1.7 + - Internet Explorer 11.483.150630 ## Features Supported @@ -69,25 +67,25 @@ Since we designed the product from the perspective of well-known use cases, whic So, what is supported: -* Basic CloudStack zones with virtual router -* Security groups -* KVM Hypervisor -* Security group templates -* Multiple zones -* CloudStackAccount Domains -* Virtual machine standard operations supported by Apache CloudStack -* Root and Data disks management -* Ad-hoc snapshots for disks -* Affinity groups management -* VM groups -* Localization support -* Frontend Themes, Custom VM colors -* Custom and Fixed service and disk offerings -* Password management -* SSH keys management -* API keys management -* Accounts management -* A lot of small improvements which affect user experience greatly +- Basic CloudStack zones with virtual router +- Security groups +- KVM Hypervisor +- Security group templates +- Multiple zones +- CloudStackAccount Domains +- Virtual machine standard operations supported by Apache CloudStack +- Root and Data disks management +- Ad-hoc snapshots for disks +- Affinity groups management +- VM groups +- Localization support +- Frontend Themes, Custom VM colors +- Custom and Fixed service and disk offerings +- Password management +- SSH keys management +- API keys management +- Accounts management +- A lot of small improvements which affect user experience greatly ## Plugins Supported @@ -97,7 +95,7 @@ Pulse Plugin is designed for visualization of virtual machines performance stati Pulse allows users of Apache CloudStack to monitor current and previous operational states of virtual machines. The plugin supports various view scales like minutes, hours, days and enables data overlays to monitor peak and average values. -We consider this plugin very important for the CloudStack ecosystem as currently there is no built-in functionality to track VM operational states, although it is vital for system administrators to successfully operate virtual servers. Read more about Plugin deployment [here](https://github.com/bwsw/cloudstack-ui/wiki/Pulse-Plugin#how-to-deploy-and-configure-pulse-plugin). +We consider this plugin very important for the CloudStack ecosystem as currently there is no built-in functionality to track VM operational states, although it is vital for system administrators to successfully operate virtual servers. Read more about Plugin deployment [here](https://github.com/bwsw/cloudstack-ui/wiki/Pulse-Plugin#how-to-deploy-and-configure-pulse-plugin). **WebShell Plugin** @@ -109,25 +107,25 @@ This feature is not available in basic CloudStack UI and API. Plugin deployment We intensively use features like projects in our own CloudStack cloud to manage resources dedicated to project groups, etc. but generic users don’t need them, so we don’t support the following features yet: -* Advanced Zones -* Hypervisors other than KVM have not been tested +- Advanced Zones +- Hypervisors other than KVM have not been tested ## Current To Dos -* Responsive interface for smart devices +- Responsive interface for smart devices ## Long Term To Dos -* Plugins - * Resource utilization stats, traffic, IO stats, CS entities stats a.k.a. Accounting - * Self registration for public cloud - * RDP/VNC (guacamole) - * Log View plugin +- Plugins + - Resource utilization stats, traffic, IO stats, CS entities stats a.k.a. Accounting + - Self registration for public cloud + - RDP/VNC (guacamole) + - Log View plugin ## Far Away To Dos -* Plugins - * Applications a.k.a. Roller (Docker swarm or Ansible, tbd) +- Plugins + - Applications a.k.a. Roller (Docker swarm or Ansible, tbd) ## Screenshots & Features Descriptions @@ -156,6 +154,7 @@ We changed a new virtual machine screen a lot. Now it’s a one-step dialog and Our team has made a big contribution to the improvement of UX when creating a virtual machine. First of all, a user now has an access to the list of all creation steps. Depending on installation source (ISO or a Template) system allows getting not only a login, password, and IP of the machine but also an access to VM interaction interface. Currently supported: + - VNC console, - WebShell if VM has a csui.vm.auth-mode tag with SSH value. To configure access to VM using WebShell, please refer to [wiki](https://github.com/bwsw/cloudstack-ui/wiki/Tags), - Access via HTTP if VM has a csui.vm.auth-mode tag with HTTP value. To configure access to VM via HTTP, please refer to [wiki](https://github.com/bwsw/cloudstack-ui/wiki/Tags). @@ -165,7 +164,7 @@ Currently supported: #### Resource usage bar -We also decided to place the resource usage bar on the same virtual machine view screen. It can be collapsed or displayed. A resource usage bar allows switching between "used" and "free" presentations to help users understanding capabilities in a better way. Domain administrators can also choose between Account and Domain view. +We also decided to place the resource usage bar on the same virtual machine view screen. It can be collapsed or displayed. A resource usage bar allows switching between "used" and "free" presentations to help users understanding capabilities in a better way. Domain administrators can also choose between Account and Domain view. ![New Virtual Machine View](./screens/resource_UsageBar_mini.png) @@ -196,14 +195,13 @@ Here the action box also allows a user to create template or volume from the sna ![Snapshots view 2](./screens/snapshotsDetails_mini.png) -#### Firewall +#### Firewall Firewall section includes three views: Firewall templates, Shared security groups and Private security groups. +It is important to understand the concept of Firewall _templates_. This is a preset of rules that can be system default or developed by a user. System administrators can specify default presets during the interface deployment in the json configuration file. Upon VM creation the system uses a default security group defined in the configuration file, or a user can create a new security group right in the VM creation form. Next, when a user changes the rules for a certain virtual machine, they don’t affect other machines. These changed rules make a _private_ security group used for that virtual machine only. -It is important to understand the concept of Firewall *templates*. This is a preset of rules that can be system default or developed by a user. System administrators can specify default presets during the interface deployment in the json configuration file. Upon VM creation the system uses a default security group defined in the configuration file, or a user can create a new security group right in the VM creation form. Next, when a user changes the rules for a certain virtual machine, they don’t affect other machines. These changed rules make a *private* security group used for that virtual machine only. - -The second way is to use a *shared* security group - a group that is used by other VMs. +The second way is to use a _shared_ security group - a group that is used by other VMs. Users can manage security group rules in two modes: a "view" mode with filtering by types and protocols and an “edit” mode. Security groups editing is available when switching from "view" mode to "editing" mode. If editing a shared group, a user is warned that changes will affect other VMs using this group. This behavior allows avoiding undesirable changes for other VMs. @@ -212,7 +210,7 @@ Users can manage security group rules in two modes: a "view" mode with filtering #### Activity Log -It’s a simplified view for account activities. It lets you choose the date and levels and see all of them. It’s close to the same screen in the ACS native UI, but we believe that the user is interested in the events of specific date and scrolling a huge event log back to find something is not productive. Sometimes the HelpDesk service just wants to show a user that something had happened on a specific date, and thus the interface allows you to find information easier. +It’s a simplified view for account activities. It lets you choose the date and levels and see all of them. It’s close to the same screen in the ACS native UI, but we believe that the user is interested in the events of specific date and scrolling a huge event log back to find something is not productive. Sometimes the HelpDesk service just wants to show a user that something had happened on a specific date, and thus the interface allows you to find information easier. ![Activity Log screen](./screens/activityLog_mini.png) @@ -238,7 +236,7 @@ Before you start, please, prepare Node development environment. Install Node.js ### Download and install dependencies 1. Clone the CS-UI project from GitHub. -2. Run ```npm install``` command. This command installs all dependencies, which are used in the project. Also, you may use ```yarn``` command. +2. Run `npm install` command. This command installs all dependencies, which are used in the project. Also, you may use `yarn` command. 3. Add your own `proxy-conf.js` file in the project root folder and set the API endpoint in this file. See [proxy-conf-example](https://github.com/bwsw/cloudstack-ui/blob/master/proxy-conf-example.js). ``` @@ -249,11 +247,11 @@ npm install ### Main commands -| command | action | -|---------|--------| -|npm test | use this command to execute tests via Karma| -|npm run build| use this command to build the project, the build artifacts will be stored in the "dist/cloudstack-ui" directory| -|npm start| use this command to compile the application, it will be available at URL - "localhost:8080".| +| command | action | +| ------------- | --------------------------------------------------------------------------------------------------------------- | +| npm test | use this command to execute tests via Karma | +| npm run build | use this command to build the project, the build artifacts will be stored in the "dist/cloudstack-ui" directory | +| npm start | use this command to compile the application, it will be available at URL - "localhost:8080". | ## Deployment @@ -262,18 +260,20 @@ npm install To run docker container use: ``` +docker pull bitworks.software:8443/cloudstack-ui:1.411.22 + docker run -d -p 80:80 --name cloudstack-ui \ -e CLIENT_ENDPOINT=http://cloudstack/client \ -e BASE_HREF=base_href \ -v /path/to/config.json:/static/config/config.json \ -           bwsw/cloudstack-ui:1.411.22 +           bitworks.software:8443/cloudstack-ui:1.411.22 ``` `http://cloudstack/client` - URL of CloudStack client endpoint (e.g. http://host:8080/client) `base_href` - custom base URL (optional, defaults to "/") -`/path/to/config.json` - path to a custom configuration file named ```config.json``` (optional) +`/path/to/config.json` - path to a custom configuration file named `config.json` (optional) Additionally, you can change favicon and CloudStack logo on login screen and in the sidebar: @@ -285,12 +285,6 @@ Additionally, you can change favicon and CloudStack logo on login screen and in where the `favicon.ico` is the favicon, `cloudstack_logo.png` is the logo displayed on the login screen and `cloudstack_logo_light.png` is the CloudStack logo displayed in the sidebar with the light theme. -### Assisting object cleanup container - -Some operations implemented in the UI require "delayed" activities, so we use additional cleaner container that cleans objects marked for the removal. - -Download and start [bwsw/cloudstack-ui-cleaner](https://hub.docker.com/r/bwsw/cloudstack-ui-cleaner/) container. - ## Versioning ``` @@ -318,7 +312,7 @@ The project is currently supported by [Bitworks Software](https://bitworks.softw You can contribute to the project development in various ways: 1. Share the information about the project with other people, try to install the UI and share your opinion with us and your colleagues. -2. Propose useful features. Ideas are always welcome. +2. Propose useful features. Ideas are always welcome. 3. Deploy it somewhere and inform us about your success story, and we will share it in the adopters section. 4. Fix bugs and send us the PR. 5. Implement a feature from the Roadmap or simply make something new. @@ -327,7 +321,7 @@ You can contribute to the project development in various ways: 1. KVM with RBD 2. Xen with NFS, Local, RBD 3. Oher browsers and operating systems -7. Hire us for frontend or backend development of custom software development projects. Take a look at our [website](https://bitworks.software/) to know where we can be useful. Take a look at our [presentation](https://www.slideshare.net/secret/BpNGxtaPUfOIqj) to learn more about us. +8. Hire us for frontend or backend development of custom software development projects. Take a look at our [website](https://bitworks.software/) to know where we can be useful. Take a look at our [presentation](https://www.slideshare.net/secret/BpNGxtaPUfOIqj) to learn more about us. To contribute, just contact us via e-mail: info@bw-sw.com diff --git a/angular.json b/angular.json index a51c4b2ae8..d6241c3cea 100644 --- a/angular.json +++ b/angular.json @@ -25,13 +25,7 @@ "main": "src/main.ts", "tsConfig": "src/tsconfig.app.json", "polyfills": "src/polyfills.ts", - "assets": [ - "src/config", - "src/i18n", - "src/css", - "src/img", - "src/agreements" - ], + "assets": ["src/config", "src/i18n", "src/css", "src/img", "src/agreements"], "styles": [ "node_modules/@mdi/font/css/materialdesignicons.min.css", "src/style/app.scss" @@ -96,30 +90,20 @@ "polyfills": "src/polyfills.ts", "tsConfig": "src/tsconfig.spec.json", "karmaConfig": "src/karma.conf.js", + "sourceMap": true, "styles": [ "node_modules/@mdi/font/css/materialdesignicons.min.css", "src/style/app.scss" ], "scripts": [], - "assets": [ - "src/config", - "src/i18n", - "src/css", - "src/img", - "src/agreements" - ] + "assets": ["src/config", "src/i18n", "src/css", "src/img", "src/agreements"] } }, "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": [ - "src/tsconfig.app.json", - "src/tsconfig.spec.json" - ], - "exclude": [ - "**/node_modules/**" - ] + "tsConfig": ["src/tsconfig.app.json", "src/tsconfig.spec.json"], + "exclude": ["**/node_modules/**"] } } } @@ -144,12 +128,8 @@ "lint": { "builder": "@angular-devkit/build-angular:tslint", "options": { - "tsConfig": [ - "e2e/tsconfig.e2e.json" - ], - "exclude": [ - "**/node_modules/**" - ] + "tsConfig": ["e2e/tsconfig.e2e.json"], + "exclude": ["**/node_modules/**"] } } } diff --git a/config-guide.md b/config-guide.md index 1008359d0e..d45c96f449 100644 --- a/config-guide.md +++ b/config-guide.md @@ -2,67 +2,71 @@ You can see examples of the configurations in the [config-example.json](https://github.com/bwsw/cloudstack-ui/blob/master/src/config/config-example.json) -* [General](#general) - * [Default Domain](#default-domain) - * [Session Refresh Interval](#session-refresh-interval) - * [API Doc Link](#api-doc-link) - * [Extensions](#extensions) -* [Virtual Machines Settings](#virtual-machines-settings) - * [VM Colors](#vm-colors) -* [Firewall (Security Groups) Settings](#firewall-security-groups-settings) - * [Security Group Templates](#security-group-templates) - * [Default Security Group Name](#default-security-group-name) -* [Images Settings](#images-settings) - * [Image Groups](#image-groups) -* [User App Settings](#user-app-settings) - * [Default First Day Of Week](#default-first-day-of-week) - * [Default Interface Language](#default-interface-language) - * [Default Time Format](#default-time-format) - * [Default Theme](#default-theme) - * [Session Timeout](#session-timeout) -* [Menu Settings](#menu-settings) - * [Allow Reordering Sidenav](#allow-reordering-sidenav) - * [Configure Sidenav](#configure-sidenav) -* [Service Offering Setting](#service-offering-setting) - * [Custom Compute Offering Parameters](#custom-compute-offering-parameters) - * [Default Compute Offering](#default-compute-offering) - * [Offering Compatibility Policy](#offering-compatibility-policy) - * [Compute Offering Classes](#compute-offering-classes) - * [Service Offering Availability](#service-offering-availability) - +- [General](#general) + - [Default Domain](#default-domain) + - [Session Refresh Interval](#session-refresh-interval) + - [API Doc Link](#api-doc-link) + - [Extensions](#extensions) +- [Virtual Machines Settings](#virtual-machines-settings) + - [VM Colors](#vm-colors) +- [Firewall (Security Groups) Settings](#firewall-security-groups-settings) + - [Security Group Templates](#security-group-templates) + - [Default Security Group Name](#default-security-group-name) +- [Images Settings](#images-settings) + - [Image Groups](#image-groups) +- [User App Settings](#user-app-settings) + - [Default First Day Of Week](#default-first-day-of-week) + - [Default Interface Language](#default-interface-language) + - [Default Time Format](#default-time-format) + - [Default Theme](#default-theme) + - [Session Timeout](#session-timeout) +- [Service Offering Setting](#service-offering-setting) + - [Custom Compute Offering Parameters](#custom-compute-offering-parameters) + - [Default Compute Offering](#default-compute-offering) + - [Offering Compatibility Policy](#offering-compatibility-policy) + - [Compute Offering Classes](#compute-offering-classes) + - [Service Offering Availability](#service-offering-availability) ## General ### Default Domain + A default domain is used to fill in the 'Domain' field in the login form. The default value is an empty value. For example, + ``` "defaultDomain": "domain" ``` ### Session Refresh Interval + The session refresh interval sends a request to the server at the specified interval (_in seconds_) to maintain an active session. The default value is `60`. For example, + ``` "sessionRefreshInterval": 60 ``` ### API Doc Link + A URL address to the API documentation. This address is displayed in the "Settings" section. The default value is a link to the Apache Cloudstack API. For example, + ``` "apiDocLink": "https://cloudstack.apache.org/api/apidocs-4.11/" ``` ### Extensions + Allows you to enable plugins. By default, all plugins are disabled. For example, + ``` "extensions": { "webShell": true, @@ -75,10 +79,12 @@ Please check [Wiki](https://github.com/bwsw/cloudstack-ui/wiki/Plugins) for a de ## Virtual Machines Settings ### VM Colors -Allows you to predefine a set of colors for virtual machines in the hexadecimal format. + +Allows you to predefine a set of colors for virtual machines in the hexadecimal format. You can specify any colors you like. For example, + ``` "vmColors": [ { "value": "#F44336" }, @@ -92,11 +98,13 @@ For example, ## Firewall (Security Groups) Settings ### Security Group Templates -Predefined templates for security groups. You can define your own security groups that will be available for all users by default. + +Predefined templates for security groups. You can define your own security groups that will be available for all users by default. By default, there are no predefined templates. For example, + ``` "securityGroupTemplates": [ { @@ -144,16 +152,16 @@ For example, Parameters: -* id -* name -* description -* preselected (true or false) - specifies whether network rules from this template will be automatically selected in the VM creation form -* ingress and egress rules (ingressrule and egressrule respectively): - * ruleid: a unique identifier - * protocol: either 'tcp', 'udp' or 'icmp' - * cidr: subnet mask (e.g. 0.0.0.0/0) - * For TCP and UDP: startport and endport - * For ICMP: icmpcode and icmptype +- id +- name +- description +- preselected (true or false) - specifies whether network rules from this template will be automatically selected in the VM creation form +- ingress and egress rules (ingressrule and egressrule respectively): + - ruleid: a unique identifier + - protocol: either 'tcp', 'udp' or 'icmp' + - cidr: subnet mask (e.g. 0.0.0.0/0) + - For TCP and UDP: startport and endport + - For ICMP: icmpcode and icmptype ### Default Security Group Name @@ -161,6 +169,7 @@ Allow you to rename the Default Firewall group. The default name is `default` for both languages. For example, + ``` "defaultSecurityGroupName": { "en": "default name", @@ -171,14 +180,16 @@ For example, ## Images Settings ### Image Groups -Allows you to define groups for installation sources (templates and ISOs). -An image group has a required `id` parameter and an optional `translations` parameter. +Allows you to define groups for installation sources (templates and ISOs). + +An image group has a required `id` parameter and an optional `translations` parameter. If there are no translations defined for the template group, the group ID will be used. By default, there are no predefined image groups. For example, + ``` "imageGroups": [ { @@ -194,97 +205,83 @@ For example, ## User App Settings ### Default First Day Of Week -Allows you to predefine the setting of the first day in the app. Possible values: + +Allows you to predefine the setting of the first day in the app. Possible values: + - 0 - Sunday - 1 - Monday (default) -For example, +For example, + ``` "defaultFirstDayOfWeek": 0 ``` ### Default Interface Language -Allows you to predefine the setting of the app interface language. Possible values: + +Allows you to predefine the setting of the app interface language. Possible values: + - "en" (default) - "ru" -For example, +For example, + ``` "defaultInterfaceLanguage": "en" ``` ### Default Time Format + Allows you to predefine the setting of the time format. Possible values: + - "auto" - value depends on the interface language (default) - "hour12" - 12-hour time - "hour24" - 24-hour time -For example, +For example, + ``` "defaultTimeFormat": "hour24" ``` ### Default Theme + Allows you to predefine the setting of the theme. Available themes are: + - "blue-red" (default) - "indigo-pink" For example, + ``` "defaultTheme": "blue-red" ``` ### Session Timeout -Allows you to predefine the setting of the session timeout. + +Allows you to predefine the setting of the session timeout. This setting determines the number of minutes the user's session should stay active. -After this time passes a user is logged out. +After this time passes a user is logged out. You can set it to `0` to turn it off, although in this case the session is likely to expire on the server side. The default value is `30`. For example, -``` -"sessionTimeout": 30 -``` - -## Menu Settings - -### Allow Reordering Sidenav -A boolean value which allows or forbids a user to reorder links in the left-side menu. -``` - "allowReorderingSidenav": false -``` - -### Configure Sidenav -Allows you to predefine the order and visibility of menu elements. -The order and the presence of the left-side menu elements is determined by the order of the elements in the array. -The VMS menu element can not be made invisible, the visibility property will be ignored. -For the configuration, you must specify all menu elements and the "allowReorderingSidenav" parameter must be true. -For example (default values), ``` -"allowReorderingSidenav": true, -"configureSidenav": [ - { "id": "VMS", "visible": true }, - { "id": "VOLUMES", "visible": true }, - { "id": "TEMPLATES", "visible": true }, - { "id": "SNAPSHOTS", "visible": true }, - { "id": "SGS", "visible": true }, - { "id": "EVENTS", "visible": true }, - { "id": "SSH", "visible": true }, - { "id": "ACCOUNTS", "visible": true }, - { "id": "SETTINGS", "visible": true } - ] +"sessionTimeout": 30 ``` ## Service Offering Setting ### Custom Compute Offering Parameters + Allows you to specify default values and limits for custom compute offering hardware parameters in VM creation. -By default, all compute offerings have the minimum restrictions of "1" CPU number, "1000" CPU speed, "512" memory -and the maximum values are not limited, default values are equal to minimum restrictions. +By default, all compute offerings have the minimum restrictions of "1" CPU number, "1000" CPU speed, "512" memory +and the maximum values are not limited, default values are equal to minimum restrictions. For example, + ``` "customComputeOfferingParameters": [ { @@ -309,9 +306,11 @@ For example, ``` ### Default Compute Offering + Allows you to specify compute offering that will be automatically preselected in the VM creation form for each zone. For example, + ``` "defaultComputeOffering": [ { @@ -322,6 +321,7 @@ For example, ``` ### Offering Compatibility Policy + This configuration allows you to restrict compute offering change based on the compute offering host tags. This is very useful when you have several clusters in one zone and you want to protect a user from converting @@ -329,8 +329,9 @@ offerings between incompatible states because it might happen that selected offe current VM relates to. Available change policies: + - "contains-all" - exact tags match -- "exactly-match" - old offering tags are subset new offering tags +- "exactly-match" - old offering tags are subset new offering tags - "no-restrictions" (default) You can ignore tags that don't influence compatibility with `offeringChangePolicyIgnoreTags` property. @@ -343,11 +344,13 @@ You can ignore tags that don't influence compatibility with `offeringChangePolic ``` ### Compute Offering Classes + Allows you to group compute offerings into classes when choosing a compute offering in the VM creation form. By default, there are no predefined compute offering classes. For example, + ``` "computeOfferingClasses": [ { @@ -382,12 +385,14 @@ For example, ``` ### Service Offering Availability + Allows you to specify which service offerings will be available for which zones. If `filterOfferings` is set to `false`, all offerings will be available for all zones. By default, `filterOfferings` is set to `false`. For example, + ``` "serviceOfferingAvailability": { "filterOfferings": true, @@ -398,4 +403,4 @@ For example, } } } -``` \ No newline at end of file +``` diff --git a/e2e/login.e2e-spec.ts b/e2e/login.e2e-spec.ts index 944861c170..a1b317d275 100644 --- a/e2e/login.e2e-spec.ts +++ b/e2e/login.e2e-spec.ts @@ -2,7 +2,6 @@ import { Login } from './pages/login.po'; import { browser } from 'protractor'; import { VMList } from './pages/vm-list.po'; - describe('e2e-test-login', () => { let page: Login; let vmlist: VMList; @@ -22,15 +21,15 @@ describe('e2e-test-login', () => { }); it('Can not login by incorrect password', () => { - page.setLogin(page.e2e_login); + page.setLogin(page.e2eLogin); page.setPassword('incorrect'); page.clickLogin(); page.checkUrlToContain('login'); }); it('Can not login by Uppercase password', () => { - page.setLogin(page.e2e_login); - page.setPassword(page.e2e_pass.toUpperCase()); + page.setLogin(page.e2eLogin); + page.setPassword(page.e2ePass.toUpperCase()); page.clickLogin(); page.checkUrlToContain('login'); }); @@ -39,8 +38,8 @@ describe('e2e-test-login', () => { // Нужно либо создавать нового пользователя, либо отказываться от этой проверки /* it('Can not login by Lowercase password', () => { - page.setLogin(page.e2e_login); - page.setPassword(page.e2e_pass.toLowerCase()); + page.setLogin(page.e2eLogin); + page.setPassword(page.e2ePass.toLowerCase()); page.clickLogin(); page.checkUrlToContain('login'); }); @@ -48,19 +47,19 @@ describe('e2e-test-login', () => { it('Can not login by incorrect login', () => { page.setLogin('incorrect'); - page.setPassword(page.e2e_pass); + page.setPassword(page.e2ePass); page.clickLogin(); page.checkUrlToContain('login'); }); it('Login is required', () => { - page.setPassword(page.e2e_pass); + page.setPassword(page.e2ePass); page.clickLogin(); page.checkUrlToContain('login'); }); it('Password is required', () => { - page.setLogin(page.e2e_login); + page.setLogin(page.e2eLogin); page.clickLogin(); page.checkUrlToContain('login'); }); @@ -86,7 +85,7 @@ describe('e2e-test-login', () => { vmlist.cancelVMPropose(); page.logout(); page.waitUrlContains('login'); - browser.get(browser.baseUrl + '/instances'); + browser.get(`${browser.baseUrl}/instances`); page.waitUrlContains('login'); page.checkUrlToContain('login'); }); diff --git a/e2e/pages/app.po.ts b/e2e/pages/app.po.ts index 9831c8e759..5fb65b852c 100644 --- a/e2e/pages/app.po.ts +++ b/e2e/pages/app.po.ts @@ -1,7 +1,6 @@ import { browser, by, element, protractor } from 'protractor'; export class CloudstackUiPage { - navigateTo() { return browser.get('/'); } @@ -11,22 +10,28 @@ export class CloudstackUiPage { } clickButtonbyClass(classname: string) { - element(by.css('.' + classname)).click(); + element(by.css(`.${classname}`)).click(); } - waitUrlContains (expected: string) { - return browser.wait(protractor.ExpectedConditions.urlContains(browser.baseUrl + '/' + expected), 5000); + waitUrlContains(expected: string) { + return browser.wait( + protractor.ExpectedConditions.urlContains(`${browser.baseUrl}/${expected}`), + 5000, + ); } buttonIsClickable(nameButton: string) { return element(by.buttonText(nameButton)).isEnabled(); } - waitRedirect (expected) { - return browser.wait(protractor.ExpectedConditions.urlIs(browser.baseUrl + '/' + expected), 5000); + waitRedirect(expected) { + return browser.wait( + protractor.ExpectedConditions.urlIs(`${browser.baseUrl}/${expected}`), + 5000, + ); } - checkUrlToContain (expected) { - return expect(browser.getCurrentUrl()).toContain(browser.baseUrl + '/' + expected); + checkUrlToContain(expected) { + return expect(browser.getCurrentUrl()).toContain(`${browser.baseUrl}/${expected}`); } } diff --git a/e2e/pages/login.po.ts b/e2e/pages/login.po.ts index 8e2643ce13..1fb0086dbd 100644 --- a/e2e/pages/login.po.ts +++ b/e2e/pages/login.po.ts @@ -3,15 +3,14 @@ import { by, element } from 'protractor'; import { CloudstackUiPage } from './app.po'; export class Login extends CloudstackUiPage { - - e2e_login = 'admin'; - e2e_pass = 'password'; + e2eLogin = 'admin'; + e2ePass = 'password'; setLogin(login: string) { element(by.name('username')).sendKeys(login); - }; + } - setPassword (pass: string) { + setPassword(pass: string) { element(by.name('password')).sendKeys(pass); } @@ -24,20 +23,23 @@ export class Login extends CloudstackUiPage { } openDomainField() { - element(by.css('.domains-visible')).isPresent().then(function () { - return; - }).catch(function () { - element(by.css('.options-button.mat-icon-button')).click(); - }); + element(by.css('.domains-visible')) + .isPresent() + .then(() => { + return; + }) + .catch(() => { + element(by.css('.options-button.mat-icon-button')).click(); + }); } - clickLogin () { + clickLogin() { return this.clickButtonbyText('Login'); } login() { - this.setLogin(this.e2e_login); - this.setPassword(this.e2e_pass); + this.setLogin(this.e2eLogin); + this.setPassword(this.e2ePass); this.clickLogin(); } diff --git a/e2e/pages/vm-list.po.ts b/e2e/pages/vm-list.po.ts index bd66d2f52b..0ba4ce5156 100644 --- a/e2e/pages/vm-list.po.ts +++ b/e2e/pages/vm-list.po.ts @@ -2,14 +2,21 @@ import { CloudstackUiPage } from './app.po'; import { browser, by, element, protractor } from 'protractor'; export class VMList extends CloudstackUiPage { - - cancelVMPropose () { - element(by.css('.cdk-overlay-pane')).isPresent().then(function () { - return element.all(by.css('.mat-button.mat-primary.ng-star-inserted')).get(1).click(); - }); + cancelVMPropose() { + element(by.css('.cdk-overlay-pane')) + .isPresent() + .then(() => { + return element + .all(by.css('.mat-button.mat-primary.ng-star-inserted')) + .get(1) + .click(); + }); } - waitVMPropose () { - return browser.wait(protractor.ExpectedConditions.presenceOf(element(by.css('.cdk-overlay-pane'))), 5000); + waitVMPropose() { + return browser.wait( + protractor.ExpectedConditions.presenceOf(element(by.css('.cdk-overlay-pane'))), + 5000, + ); } } diff --git a/e2e/protractor.conf.js b/e2e/protractor.conf.js index d5dd27f692..46f75d196e 100644 --- a/e2e/protractor.conf.js +++ b/e2e/protractor.conf.js @@ -5,9 +5,7 @@ const { SpecReporter } = require('jasmine-spec-reporter'); exports.config = { allScriptsTimeout: 15000, - specs: [ - './**/*.e2e-spec.ts' - ], + specs: ['./**/*.e2e-spec.ts'], /* multiCapabilities: [{ 'browserName': 'firefox', @@ -22,11 +20,11 @@ exports.config = { }], */ capabilities: { - 'browserName': 'chrome' + browserName: 'chrome', }, suites: { - login: './e2e/login.e2e-spec.ts' + login: './e2e/login.e2e-spec.ts', }, directConnect: true, baseUrl: 'http://localhost:8081', @@ -34,12 +32,12 @@ exports.config = { jasmineNodeOpts: { showColors: true, defaultTimeoutInterval: 30000, - print: function() {} + print: function() {}, }, onPrepare() { require('ts-node').register({ - project: 'e2e/tsconfig.e2e.json' + project: 'e2e/tsconfig.e2e.json', }); jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); - } + }, }; diff --git a/package.json b/package.json index 58fe7f5858..9d8a496eb9 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,11 @@ "coveralls": "cat ./reports/coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s" }, + "husky": { + "hooks": { + "pre-commit": "pretty-quick --staged" + } + }, "private": true, "dependencies": { "@angular/animations": "6.1.8", @@ -70,6 +75,7 @@ "conventional-changelog": "^2.0.3", "conventional-changelog-cli": "^2.0.5", "coveralls": "^3.0.2", + "husky": "^1.1.2", "jasmine-core": "~2.99.1", "jasmine-marbles": "^0.4.0", "jasmine-spec-reporter": "~4.2.1", @@ -81,9 +87,13 @@ "karma-junit-reporter": "^1.2.0", "karma-mocha-reporter": "^2.2.3", "ngrx-store-freeze": "^0.2.0", + "prettier": "1.14.3", + "pretty-quick": "^1.8.0", "protractor": "~5.3.0", "ts-node": "~5.0.1", - "tslint": "~5.9.1", + "tslint": "~5.11.0", + "tslint-config-airbnb": "^5.11.0", + "tslint-config-prettier": "^1.15.0", "typescript": "~2.7.2" } } diff --git a/proxy-conf-example.js b/proxy-conf-example.js index fafc60a172..ddbb5bc048 100644 --- a/proxy-conf-example.js +++ b/proxy-conf-example.js @@ -7,28 +7,25 @@ const PULSE_PLUGIN_ENDPOINT = 'http://example.com:8081'; const WEBSHELL_PLUGIN_ENDPOINT = 'http://example.com:8082'; function onProxyRes(proxyRes, req, res) { - var cookies = proxyRes.headers[ 'set-cookie' ]; + var cookies = proxyRes.headers['set-cookie']; var cookieRegex = /Secure/i; if (cookies) { - var newCookie = cookies.map(function (cookie) { + var newCookie = cookies.map(function(cookie) { if (cookieRegex.test(cookie)) { return cookie.replace(cookieRegex, ''); } return cookie; }); - delete proxyRes.headers[ 'set-cookie' ]; - proxyRes.headers[ 'set-cookie' ] = newCookie; + delete proxyRes.headers['set-cookie']; + proxyRes.headers['set-cookie'] = newCookie; } } const apiProxyConfig = { - context: [ - '/client/api', - '/client/console' - ], + context: ['/client/api', '/client/console'], target: CLOUDSTACK_ENDPOINT, - secure: false + secure: false, }; // If server works over https need to change Secure Cookie @@ -37,18 +34,18 @@ if (CLOUDSTACK_ENDPOINT.indexOf('https') === 0) { } const pulseProxyConfig = { - context: [ '/cs-extensions/pulse/**' ], + context: ['/cs-extensions/pulse/**'], target: PULSE_PLUGIN_ENDPOINT, secure: false, - pathRewrite: { '^/cs-extensions/pulse': '' } + pathRewrite: { '^/cs-extensions/pulse': '' }, }; const webShellProxyConfig = { - context: [ '/cs-extensions/webshell/**' ], + context: ['/cs-extensions/webshell/**'], target: WEBSHELL_PLUGIN_ENDPOINT, secure: false, - pathRewrite: { '^/cs-extensions/webshell': '' } -} + pathRewrite: { '^/cs-extensions/webshell': '' }, +}; const PROXY_CONFIG = [ apiProxyConfig, diff --git a/src/app/account/account-container/account-actions.container.ts b/src/app/account/account-container/account-actions.container.ts index 056fcae173..badc6e69ce 100644 --- a/src/app/account/account-container/account-actions.container.ts +++ b/src/app/account/account-container/account-actions.container.ts @@ -1,7 +1,4 @@ -import { - Component, - Input -} from '@angular/core'; +import { Component, Input } from '@angular/core'; import { DialogService } from '../../dialog/dialog-service/dialog.service'; import { Store } from '@ngrx/store'; import { State } from '../../reducers/index'; @@ -9,27 +6,22 @@ import { State } from '../../reducers/index'; import * as accountActions from '../../reducers/accounts/redux/accounts.actions'; import { Account } from '../../shared/models/account.model'; - @Component({ selector: 'cs-account-action-container', template: ` `, }) export class AccountActionsContainerComponent { + @Input() + public account: Account; - @Input() public account: Account; - - constructor( - public dialogService: DialogService, - private store: Store, - ) { - } + constructor(public dialogService: DialogService, private store: Store) {} public onAccountEnable(account: Account): void { this.store.dispatch(new accountActions.EnableAccountRequest(account)); diff --git a/src/app/account/account-container/account-creation.container.ts b/src/app/account/account-container/account-creation.container.ts index 1298b2c999..118f2c24af 100644 --- a/src/app/account/account-container/account-creation.container.ts +++ b/src/app/account/account-container/account-creation.container.ts @@ -1,6 +1,6 @@ import { Component } from '@angular/core'; import { DialogService } from '../../dialog/dialog-service/dialog.service'; -import { Store } from '@ngrx/store'; +import { select, Store } from '@ngrx/store'; import { State } from '../../reducers/index'; import * as accountActions from '../../reducers/accounts/redux/accounts.actions'; @@ -9,7 +9,6 @@ import * as fromDomains from '../../reducers/domains/redux/domains.reducers'; import * as fromRoles from '../../reducers/roles/redux/roles.reducers'; import { AccountData } from '../../shared/models/account.model'; - @Component({ selector: 'cs-account-creation-container', template: ` @@ -17,20 +16,16 @@ import { AccountData } from '../../shared/models/account.model'; [isLoading]="loading$ | async" [domains]="domains$ | async" [roles]="roles$ | async" - (onAccountCreate)="createAccount($event)" + (accountCreated)="createAccount($event)" > `, }) export class AccountCreationContainerComponent { - public loading$ = this.store.select(fromAccounts.isLoading); - public domains$ = this.store.select(fromDomains.selectAll); - public roles$ = this.store.select(fromRoles.selectAll); + public loading$ = this.store.pipe(select(fromAccounts.isLoading)); + public domains$ = this.store.pipe(select(fromDomains.selectAll)); + public roles$ = this.store.pipe(select(fromRoles.selectAll)); - constructor( - public dialogService: DialogService, - private store: Store, - ) { - } + constructor(public dialogService: DialogService, private store: Store) {} public createAccount(data: AccountData) { this.store.dispatch(new accountActions.CreateAccount(data)); diff --git a/src/app/account/account-container/account-details.container.ts b/src/app/account/account-container/account-details.container.ts index 749baf5d2a..22045b1fa9 100644 --- a/src/app/account/account-container/account-details.container.ts +++ b/src/app/account/account-container/account-details.container.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { Store } from '@ngrx/store'; +import { select, Store } from '@ngrx/store'; import * as fromAccounts from '../../reducers/accounts/redux/accounts.reducers'; import { takeUntil } from 'rxjs/operators'; @@ -23,7 +23,7 @@ import { Account, ResourceLimit } from '../../shared/models'; ` + >`, }) export class AccountDetailsContainerComponent extends WithUnsubscribe() implements OnInit { - - readonly account$ = this.store.select(fromAccounts.getSelectedAccount); - readonly configurations$ = this.store.select(fromConfigurations.selectAll); - readonly limits$ = this.store.select(fromResourceLimits.getAllLimits); - readonly stats$ = this.store.select(fromResourceCounts.selectAll); + readonly account$ = this.store.pipe(select(fromAccounts.getSelectedAccount)); + readonly configurations$ = this.store.pipe(select(fromConfigurations.selectAll)); + readonly limits$ = this.store.pipe(select(fromResourceLimits.getAllLimits)); + readonly stats$ = this.store.pipe(select(fromResourceCounts.selectAll)); public account: Account; - - constructor( - private store: Store, - private authService: AuthService - ) { + constructor(private store: Store, private authService: AuthService) { super(); } public onConfigurationEdit(configuration) { - this.store.dispatch(new configurationAction.UpdateConfigurationRequest({ - configuration, - account: this.account - })); + this.store.dispatch( + new configurationAction.UpdateConfigurationRequest({ + configuration, + account: this.account, + }), + ); } public onLimitsUpdate(limits: ResourceLimit[]) { @@ -66,32 +63,38 @@ export class AccountDetailsContainerComponent extends WithUnsubscribe() implemen } public onStatisticsUpdate() { - this.store.dispatch(new resourceCountAction.LoadResourceCountsRequest({ - account: this.account.name, - domainid: this.account.domainid - })); + this.store.dispatch( + new resourceCountAction.LoadResourceCountsRequest({ + account: this.account.name, + domainid: this.account.domainid, + }), + ); } public ngOnInit() { - this.account$.pipe( - takeUntil(this.unsubscribe$)) - .subscribe(account => { - if (account) { - this.account = account; - this.store.dispatch(new resourceLimitAction.LoadResourceLimitsRequest({ + this.account$.pipe(takeUntil(this.unsubscribe$)).subscribe(account => { + if (account) { + this.account = account; + this.store.dispatch( + new resourceLimitAction.LoadResourceLimitsRequest({ account: account.name, - domainid: account.domainid - })); + domainid: account.domainid, + }), + ); - if (this.isAdmin()) { - this.store.dispatch(new configurationAction.LoadConfigurationsRequest({ accountid: account.id })); - this.store.dispatch(new resourceCountAction.LoadResourceCountsRequest({ + if (this.isAdmin()) { + this.store.dispatch( + new configurationAction.LoadConfigurationsRequest({ accountid: account.id }), + ); + this.store.dispatch( + new resourceCountAction.LoadResourceCountsRequest({ account: account.name, - domainid: account.domainid - })); - } + domainid: account.domainid, + }), + ); } - }); + } + }); } public isAdmin() { diff --git a/src/app/account/account-container/account-filter.container.ts b/src/app/account/account-container/account-filter.container.ts index ca0e05189e..7158189bd1 100644 --- a/src/app/account/account-container/account-filter.container.ts +++ b/src/app/account/account-container/account-filter.container.ts @@ -1,6 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Store } from '@ngrx/store'; +import { select, Store } from '@ngrx/store'; import { takeUntil } from 'rxjs/operators'; import * as accountActions from '../../reducers/accounts/redux/accounts.actions'; @@ -31,53 +31,70 @@ const FILTER_KEY = 'accountListFilters'; [selectedStates]="selectedStates$ | async" [groupings]="groupings" [selectedGroupings]="selectedGroupings" - (onDomainsChange)="onDomainsChange($event)" - (onRolesChange)="onRolesChange($event)" - (onRoleTypesChange)="onRoleTypesChange($event)" - (onStatesChange)="onStatesChange($event)" - (onGroupingsChange)="onGroupingsChange($event)" - >` + (domainsChanged)="onDomainsChange($event)" + (rolesChanged)="onRolesChange($event)" + (roleTypesChanged)="onRoleTypesChange($event)" + (statesChanged)="onStatesChange($event)" + (groupingsChanged)="onGroupingsChange($event)" + >`, }) export class AccountFilterContainerComponent extends WithUnsubscribe() implements OnInit { - @Input() groupings: Array; - @Input() selectedGroupings: Array; + @Input() + groupings: any[]; + @Input() + selectedGroupings: any[]; - readonly filters$ = this.store.select(fromAccounts.filters); - readonly loading$ = this.store.select(fromAccounts.isLoading); - readonly domains$ = this.store.select(fromDomains.selectAll); - readonly roles$ = this.store.select(fromRoles.selectAll); - readonly roleTypes$ = this.store.select(fromRoles.roleTypes); + readonly filters$ = this.store.pipe(select(fromAccounts.filters)); + readonly loading$ = this.store.pipe(select(fromAccounts.isLoading)); + readonly domains$ = this.store.pipe(select(fromDomains.selectAll)); + readonly roles$ = this.store.pipe(select(fromRoles.selectAll)); + readonly roleTypes$ = this.store.pipe(select(fromRoles.roleTypes)); - readonly selectedDomainIds$ = this.store.select(fromAccounts.filterSelectedDomainIds); - readonly selectedRoleNames$ = this.store.select(fromAccounts.filterSelectedRoleNames); - readonly selectedStates$ = this.store.select(fromAccounts.filterSelectedStates); - readonly selectedRoleTypes$ = this.store.select(fromAccounts.filterSelectedRoleTypes); + readonly selectedDomainIds$ = this.store.pipe(select(fromAccounts.filterSelectedDomainIds)); + readonly selectedRoleNames$ = this.store.pipe(select(fromAccounts.filterSelectedRoleNames)); + readonly selectedStates$ = this.store.pipe(select(fromAccounts.filterSelectedStates)); + readonly selectedRoleTypes$ = this.store.pipe(select(fromAccounts.filterSelectedRoleTypes)); - public states: Array = ['enabled', 'disabled']; + public states: string[] = ['enabled', 'disabled']; private filterService = new FilterService( { - 'domains': { type: 'array', defaultOption: [] }, - 'roles': { type: 'array', defaultOption: [] }, - 'roleTypes': { type: 'array', defaultOption: [] }, - 'states': { type: 'array', defaultOption: [] }, - 'groupings': { type: 'array', defaultOption: [] } + domains: { type: 'array', defaultOption: [] }, + roles: { type: 'array', defaultOption: [] }, + roleTypes: { type: 'array', defaultOption: [] }, + states: { type: 'array', defaultOption: [] }, + groupings: { type: 'array', defaultOption: [] }, }, this.router, this.sessionStorage, FILTER_KEY, - this.activatedRoute + this.activatedRoute, ); constructor( private store: Store, private sessionStorage: SessionStorageService, private activatedRoute: ActivatedRoute, - private router: Router + private router: Router, ) { super(); } + public ngOnInit() { + this.store.dispatch(new domainActions.LoadDomainsRequest()); + this.store.dispatch(new roleActions.LoadRolesRequest()); + this.initFilters(); + this.filters$.pipe(takeUntil(this.unsubscribe$)).subscribe(filters => { + this.filterService.update({ + domains: filters.selectedDomainIds, + roles: filters.selectedRoleNames, + roleTypes: filters.selectedRoleTypes, + states: filters.selectedStates, + groupings: filters.selectedGroupings.map(g => g.key), + }); + }); + } + public onDomainsChange(selectedDomainIds) { this.store.dispatch(new accountActions.AccountFilterUpdate({ selectedDomainIds })); } @@ -99,7 +116,6 @@ export class AccountFilterContainerComponent extends WithUnsubscribe() implement } private initFilters(): void { - const params = this.filterService.getParams(); const selectedDomainIds = params['domains']; const selectedRoleNames = params['roles']; @@ -114,31 +130,14 @@ export class AccountFilterContainerComponent extends WithUnsubscribe() implement return acc; }, []); - this.store.dispatch(new accountActions.AccountFilterUpdate({ - selectedRoleTypes, - selectedRoleNames, - selectedDomainIds, - selectedStates, - selectedGroupings - })); - + this.store.dispatch( + new accountActions.AccountFilterUpdate({ + selectedRoleTypes, + selectedRoleNames, + selectedDomainIds, + selectedStates, + selectedGroupings, + }), + ); } - - public ngOnInit() { - this.store.dispatch(new domainActions.LoadDomainsRequest()); - this.store.dispatch(new roleActions.LoadRolesRequest()); - this.initFilters(); - this.filters$.pipe( - takeUntil(this.unsubscribe$)) - .subscribe(filters => { - this.filterService.update({ - 'domains': filters.selectedDomainIds, - 'roles': filters.selectedRoleNames, - 'roleTypes': filters.selectedRoleTypes, - 'states': filters.selectedStates, - 'groupings': filters.selectedGroupings.map(g => g.key) - }); - }); - } - } diff --git a/src/app/account/account-container/account-sidebar.container.ts b/src/app/account/account-container/account-sidebar.container.ts index 7e8a98e9de..e23fd35ebe 100644 --- a/src/app/account/account-container/account-sidebar.container.ts +++ b/src/app/account/account-container/account-sidebar.container.ts @@ -1,9 +1,6 @@ -import { - Component, - OnInit -} from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { State } from '../../reducers/index'; -import { Store } from '@ngrx/store'; +import { select, Store } from '@ngrx/store'; import * as accountEvent from '../../reducers/accounts/redux/accounts.actions'; import { ActivatedRoute } from '@angular/router'; import * as fromAccounts from '../../reducers/accounts/redux/accounts.reducers'; @@ -13,21 +10,15 @@ import * as fromAccounts from '../../reducers/accounts/redux/accounts.reducers'; template: ` ` + >`, }) export class AccountSidebarContainerComponent implements OnInit { + readonly account$ = this.store.pipe(select(fromAccounts.getSelectedAccount)); - readonly account$ = this.store.select(fromAccounts.getSelectedAccount); - - - constructor( - private store: Store, - private activatedRoute: ActivatedRoute - ) { } + constructor(private store: Store, private activatedRoute: ActivatedRoute) {} public ngOnInit() { const params = this.activatedRoute.snapshot.params; this.store.dispatch(new accountEvent.LoadSelectedAccount(params['id'])); } - } diff --git a/src/app/account/account-container/account-user-edit.container.ts b/src/app/account/account-container/account-user-edit.container.ts index d034185bde..34f5eb589c 100644 --- a/src/app/account/account-container/account-user-edit.container.ts +++ b/src/app/account/account-container/account-user-edit.container.ts @@ -1,15 +1,9 @@ -import { - Component, - Inject -} from '@angular/core'; +import { Component, Inject } from '@angular/core'; import { State } from '../../reducers/index'; import { Store } from '@ngrx/store'; import { Account } from '../../shared/models/account.model'; import { AccountUser } from '../../shared/models/account-user.model'; -import { - MAT_DIALOG_DATA, - MatDialogRef -} from '@angular/material'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; import * as accountActions from '../../reducers/accounts/redux/accounts.actions'; @@ -21,7 +15,7 @@ import * as accountActions from '../../reducers/accounts/redux/accounts.actions' [confirmButtonText]="confirmButtonText" [user]="user" (updateUser)="updateUser($event)" - >` + >`, }) export class AccountUserEditContainerComponent { public account: Account; @@ -32,7 +26,7 @@ export class AccountUserEditContainerComponent { constructor( private dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) data: any, - private store: Store + private store: Store, ) { this.title = data.title; this.user = { ...data.user }; @@ -42,7 +36,7 @@ export class AccountUserEditContainerComponent { public updateUser(user: AccountUser) { if (!this.user || !this.user.id) { - user.account = this.account && this.account.name || user.username; + user.account = (this.account && this.account.name) || user.username; if (this.account) { user.domainid = this.account.domainid; diff --git a/src/app/account/account-container/account-user-password.container.ts b/src/app/account/account-container/account-user-password.container.ts index 671e637288..b28f907cbb 100644 --- a/src/app/account/account-container/account-user-password.container.ts +++ b/src/app/account/account-container/account-user-password.container.ts @@ -11,7 +11,7 @@ import * as accountActions from '../../reducers/accounts/redux/accounts.actions' template: ` ` + >`, }) export class AccountUserPasswordFormContainerComponent { public user: AccountUser; @@ -19,16 +19,18 @@ export class AccountUserPasswordFormContainerComponent { constructor( private dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) data: any, - private store: Store + private store: Store, ) { - this.user = {...data.user}; + this.user = { ...data.user }; } public changePassword(password: string) { - this.store.dispatch(new accountActions.AccountUserUpdate({ - ...this.user, - password - })); + this.store.dispatch( + new accountActions.AccountUserUpdate({ + ...this.user, + password, + }), + ); this.dialogRef.close(); } } diff --git a/src/app/account/account-container/account-users.container.ts b/src/app/account/account-container/account-users.container.ts index e47bb4b643..185651331c 100644 --- a/src/app/account/account-container/account-users.container.ts +++ b/src/app/account/account-container/account-users.container.ts @@ -1,5 +1,5 @@ import { Component } from '@angular/core'; -import { Store } from '@ngrx/store'; +import { select, Store } from '@ngrx/store'; import * as accountActions from '../../reducers/accounts/redux/accounts.actions'; import * as fromAccounts from '../../reducers/accounts/redux/accounts.reducers'; import { State } from '../../reducers/index'; @@ -12,19 +12,15 @@ import { AuthService } from '../../shared/services/auth.service'; [account]="account$ | async" [isAdmin]="isAdmin()" [currentUserId]="currentUserId()" - (onUserRegenerateKey)="generateUserKeys($event)" - (onUserDelete)="deleteUser($event)" - (onLoadUserKeys)="loadUserKeys($event)" - >` + (userRegenerateKey)="generateUserKeys($event)" + (userDeleted)="deleteUser($event)" + (loadUserKeys)="loadUserKeys($event)" + >`, }) export class AccountUsersContainerComponent { - readonly account$ = this.store.select(fromAccounts.getSelectedAccount); + readonly account$ = this.store.pipe(select(fromAccounts.getSelectedAccount)); - constructor( - private store: Store, - private authService: AuthService - ) { - } + constructor(private store: Store, private authService: AuthService) {} public isAdmin() { return this.authService.isAdmin(); diff --git a/src/app/account/account-container/account.container.ts b/src/app/account/account-container/account.container.ts index 0c2f899597..5c36f061a0 100644 --- a/src/app/account/account-container/account.container.ts +++ b/src/app/account/account-container/account.container.ts @@ -1,24 +1,16 @@ -import { - AfterViewInit, - ChangeDetectorRef, - Component, - OnInit -} from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { State } from '../../reducers/index'; -import { Store } from '@ngrx/store'; +import { select, Store } from '@ngrx/store'; import * as accountActions from '../../reducers/accounts/redux/accounts.actions'; import * as fromAccounts from '../../reducers/accounts/redux/accounts.reducers'; import { WithUnsubscribe } from '../../utils/mixins/with-unsubscribe'; import { AuthService } from '../../shared/services/auth.service'; -import { - Account, - AccountState -} from '../../shared/models/account.model'; +import { Account, accountState } from '../../shared/models/account.model'; export const stateTranslations = { - [AccountState.disabled]: 'ACCOUNT_STATE.DISABLED', - [AccountState.enabled]: 'ACCOUNT_STATE.ENABLED', + [accountState.disabled]: 'ACCOUNT_STATE.DISABLED', + [accountState.enabled]: 'ACCOUNT_STATE.ENABLED', }; @Component({ @@ -29,13 +21,13 @@ export const stateTranslations = { [isLoading]="loading$ | async" [groupings]="groupings" [selectedGroupings]="selectedGroupings$ | async" - >` + >`, }) -export class AccountPageContainerComponent extends WithUnsubscribe() implements OnInit, AfterViewInit { - - readonly accounts$ = this.store.select(fromAccounts.selectFilteredAccounts); - readonly loading$ = this.store.select(fromAccounts.isLoading); - readonly selectedGroupings$ = this.store.select(fromAccounts.filterSelectedGroupings); +export class AccountPageContainerComponent extends WithUnsubscribe() + implements OnInit, AfterViewInit { + readonly accounts$ = this.store.pipe(select(fromAccounts.selectFilteredAccounts)); + readonly loading$ = this.store.pipe(select(fromAccounts.isLoading)); + readonly selectedGroupings$ = this.store.pipe(select(fromAccounts.filterSelectedGroupings)); public groupings = [ { @@ -61,13 +53,13 @@ export class AccountPageContainerComponent extends WithUnsubscribe() implements label: 'ACCOUNT_PAGE.FILTERS.GROUP_BY_STATES', selector: (item: Account) => item.state, name: (item: Account) => this.stateTranslation(item.state), - } + }, ]; constructor( private store: Store, private authService: AuthService, - private cd: ChangeDetectorRef + private cd: ChangeDetectorRef, ) { super(); } @@ -87,5 +79,4 @@ export class AccountPageContainerComponent extends WithUnsubscribe() implements public ngAfterViewInit() { this.cd.detectChanges(); } - } diff --git a/src/app/account/account-list-filter/account-list-filter.component.html b/src/app/account/account-list-filter/account-list-filter.component.html index c96dbe2bfd..96f488e4b5 100644 --- a/src/app/account/account-list-filter/account-list-filter.component.html +++ b/src/app/account/account-list-filter/account-list-filter.component.html @@ -4,7 +4,7 @@ multiple="true" [placeholder]="'ACCOUNT_PAGE.FILTERS.SELECT_DOMAINS' | translate" [ngModel]="selectedDomainIds" - (selectionChange)="onDomainsChange.emit($event.value)" + (selectionChange)="domainsChanged.emit($event.value)" > {{ domain.name }} @@ -15,7 +15,7 @@ multiple="true" [placeholder]="'ACCOUNT_PAGE.FILTERS.SELECT_ROLES' | translate" [ngModel]="selectedRoleNames" - (selectionChange)="onRolesChange.emit($event.value)" + (selectionChange)="rolesChanged.emit($event.value)" > {{ role.name }} @@ -26,7 +26,7 @@ multiple="true" [placeholder]="'ACCOUNT_PAGE.FILTERS.SELECT_ROLE_TYPES' | translate" [ngModel]="selectedRoleTypes" - (selectionChange)="onRoleTypesChange.emit($event.value)" + (selectionChange)="roleTypesChanged.emit($event.value)" > {{ roleType }} @@ -37,7 +37,7 @@ multiple="true" [placeholder]="'ACCOUNT_PAGE.FILTERS.SELECT_STATES' | translate" [ngModel]="selectedStates" - (selectionChange)="onStatesChange.emit($event.value)" + (selectionChange)="statesChanged.emit($event.value)" > {{ stateTranslationToken(state) | translate }} @@ -50,7 +50,7 @@ [placeholder]="'ACCOUNT_PAGE.FILTERS.GROUP_BY' | translate" [ngModel]="selectedGroupings" [dragItems]="groupings" - (ngModelChange)="onGroupingsChange.emit($event)" + (ngModelChange)="groupingsChanged.emit($event)" > {{ g.label | translate }} diff --git a/src/app/account/account-list-filter/account-list-filter.component.ts b/src/app/account/account-list-filter/account-list-filter.component.ts index bd8b9b3d91..73430c95c7 100644 --- a/src/app/account/account-list-filter/account-list-filter.component.ts +++ b/src/app/account/account-list-filter/account-list-filter.component.ts @@ -5,24 +5,39 @@ import { reorderAvailableGroupings } from '../../shared/utils/reorder-groupings' @Component({ selector: 'cs-account-list-filter', - templateUrl: 'account-list-filter.component.html' + templateUrl: 'account-list-filter.component.html', }) export class AccountListFilterComponent implements OnInit { - @Input() public domains: Array; - @Input() public roleTypes: Array; - @Input() public roles: Array; - @Input() public states: Array; - @Input() public groupings: Array; - @Input() public selectedRoleTypes: string[] = []; - @Input() public selectedDomainIds: string[] = []; - @Input() public selectedRoleNames: string[] = []; - @Input() public selectedStates: string[] = []; - @Input() public selectedGroupings: Array = []; - @Output() public onDomainsChange = new EventEmitter(); - @Output() public onRolesChange = new EventEmitter(); - @Output() public onRoleTypesChange = new EventEmitter(); - @Output() public onStatesChange = new EventEmitter(); - @Output() public onGroupingsChange = new EventEmitter(); + @Input() + public domains: Domain[]; + @Input() + public roleTypes: string[]; + @Input() + public roles: Role[]; + @Input() + public states: string[]; + @Input() + public groupings: any[]; + @Input() + public selectedRoleTypes: string[] = []; + @Input() + public selectedDomainIds: string[] = []; + @Input() + public selectedRoleNames: string[] = []; + @Input() + public selectedStates: string[] = []; + @Input() + public selectedGroupings: any[] = []; + @Output() + public domainsChanged = new EventEmitter(); + @Output() + public rolesChanged = new EventEmitter(); + @Output() + public roleTypesChanged = new EventEmitter(); + @Output() + public statesChanged = new EventEmitter(); + @Output() + public groupingsChanged = new EventEmitter(); public stateTranslationToken(state): string { return stateTranslations[state]; diff --git a/src/app/account/account-list/account-list.component.ts b/src/app/account/account-list/account-list.component.ts index 88ce5095b7..055e4e58f3 100644 --- a/src/app/account/account-list/account-list.component.ts +++ b/src/app/account/account-list/account-list.component.ts @@ -1,9 +1,4 @@ -import { - Component, - EventEmitter, - Input, - Output -} from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { AccountCardItemComponent } from '../account/card-item/account-card-item.component'; import { Account } from '../../shared/models/account.model'; import { ListService } from '../../shared/components/list/list.service'; @@ -12,24 +7,28 @@ import { AccountRowItemComponent } from '../account/row-item/account-row-item.co @Component({ selector: 'cs-account-list', - templateUrl: 'account-list.component.html' + templateUrl: 'account-list.component.html', }) export class AccountListComponent { - @Input() public accounts: Array; - @Input() public groupings: Array; - @Input() public mode: ViewMode; - @Output() public viewModeChange = new EventEmitter(); + @Input() + public accounts: Account[]; + @Input() + public groupings: any[]; + @Input() + public mode: ViewMode; + @Output() + public viewModeChange = new EventEmitter(); public inputs; public outputs; constructor(public listService: ListService) { this.inputs = { - isSelected: (item: Account) => this.listService.isSelected(item.id) + isSelected: (item: Account) => this.listService.isSelected(item.id), }; this.outputs = { - onClick: this.selectAccount.bind(this) + onClick: this.selectAccount.bind(this), }; } diff --git a/src/app/account/account-page/account-page.component.html b/src/app/account/account-page/account-page.component.html index 567b96be5e..710e45c151 100644 --- a/src/app/account/account-page/account-page.component.html +++ b/src/app/account/account-page/account-page.component.html @@ -1,5 +1,5 @@ @@ -15,7 +15,7 @@
@@ -40,7 +40,7 @@
diff --git a/src/app/account/account-page/account-page.component.ts b/src/app/account/account-page/account-page.component.ts index 85b174ea16..37221cd34d 100644 --- a/src/app/account/account-page/account-page.component.ts +++ b/src/app/account/account-page/account-page.component.ts @@ -1,27 +1,25 @@ -import { - Component, - Input -} from '@angular/core'; +import { Component, Input } from '@angular/core'; import { ListService } from '../../shared/components/list/list.service'; import { Account } from '../../shared'; import { AuthService } from '../../shared/services/auth.service'; -import { - ActivatedRoute, - Router -} from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { ViewMode } from '../../shared/components/view-mode-switch/view-mode-switch.component'; @Component({ selector: 'cs-account-page', templateUrl: 'account-page.component.html', styleUrls: ['account-page.component.scss'], - providers: [ListService] + providers: [ListService], }) export class AccountPageComponent { - @Input() public accounts: Array = []; - @Input() public groupings: Array; - @Input() public isLoading: boolean; - @Input() public selectedGroupings: Array = []; + @Input() + public accounts: Account[] = []; + @Input() + public groupings: any[]; + @Input() + public isLoading: boolean; + @Input() + public selectedGroupings: any[] = []; public mode: ViewMode; public viewModeKey = 'accountPageViewMode'; @@ -30,8 +28,8 @@ export class AccountPageComponent { public listService: ListService, public authService: AuthService, private router: Router, - private activatedRoute: ActivatedRoute - ) { } + private activatedRoute: ActivatedRoute, + ) {} public isAdmin(): boolean { return this.authService.isAdmin(); @@ -40,12 +38,11 @@ export class AccountPageComponent { public showCreationDialog(): void { this.router.navigate(['./create'], { queryParamsHandling: 'preserve', - relativeTo: this.activatedRoute + relativeTo: this.activatedRoute, }); } public changeMode(mode) { this.mode = mode; } - } diff --git a/src/app/account/account-sidebar/account-details/account-details.component.ts b/src/app/account/account-sidebar/account-details/account-details.component.ts index 3d8a384a07..26ffcaabaa 100644 --- a/src/app/account/account-sidebar/account-details/account-details.component.ts +++ b/src/app/account/account-sidebar/account-details/account-details.component.ts @@ -1,23 +1,18 @@ -import { - Component, - Input -} from '@angular/core'; +import { Component, Input } from '@angular/core'; import { Account } from '../../../shared/models/account.model'; import { AuthService } from '../../../shared/services/auth.service'; @Component({ selector: 'cs-account-detail', - templateUrl: 'account-details.component.html' + templateUrl: 'account-details.component.html', }) export class AccountDetailsComponent { - @Input() public account: Account; + @Input() + public account: Account; - constructor( - private authService: AuthService - ) { } + constructor(private authService: AuthService) {} public isAdmin() { return this.authService.isAdmin(); } - } diff --git a/src/app/account/account-sidebar/account-limits/account-limits.component.ts b/src/app/account/account-sidebar/account-limits/account-limits.component.ts index fd04519752..adfa93e6d3 100644 --- a/src/app/account/account-sidebar/account-limits/account-limits.component.ts +++ b/src/app/account/account-sidebar/account-limits/account-limits.component.ts @@ -1,15 +1,13 @@ import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import * as cloneDeep from 'lodash/cloneDeep'; - import { ResourceLimit, ResourceType } from '../../../shared/models'; - @Component({ selector: 'cs-account-limits', templateUrl: 'account-limits.component.html', styleUrls: ['account-limits.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class AccountLimitsComponent { @Input() @@ -21,8 +19,10 @@ export class AccountLimitsComponent { return this._limits; } - @Input() public isAdmin: boolean; - @Output() public limitsUpdate = new EventEmitter(); + @Input() + public isAdmin: boolean; + @Output() + public limitsUpdate = new EventEmitter(); public isEdit = false; public localLimits: ResourceLimit[] = []; @@ -41,6 +41,7 @@ export class AccountLimitsComponent { [ResourceType.SecondaryStorage]: 'ACCOUNT_PAGE.CONFIGURATION.SSTORAGE_LIMIT', }; + // tslint:disable-next-line:variable-name private _limits: ResourceLimit[]; public onSave(): void { @@ -60,18 +61,18 @@ export class AccountLimitsComponent { } return { ...limit, - max: Infinity + max: Infinity, }; } private setInfinityToNoLimit(limit: ResourceLimit & { max: string | number }) { if ( - limit.max === Infinity - || (typeof limit.max === 'string' && (limit.max as string).toLowerCase() === 'infinity') + limit.max === Infinity || + (typeof limit.max === 'string' && (limit.max as string).toLowerCase() === 'infinity') ) { return { ...limit, - max: -1 + max: -1, }; } return limit; diff --git a/src/app/account/account-sidebar/account-settings/account-configuration/account-configuration.component.html b/src/app/account/account-sidebar/account-settings/account-configuration/account-configuration.component.html index ade5f55134..4e4745dcdb 100644 --- a/src/app/account/account-sidebar/account-settings/account-configuration/account-configuration.component.html +++ b/src/app/account/account-sidebar/account-settings/account-configuration/account-configuration.component.html @@ -2,5 +2,5 @@ [name]="configuration.name" [value]="configuration.value" [canBeEdit]="true" - (onButtonClicked)="edit()" + (buttonClicked)="edit()" > diff --git a/src/app/account/account-sidebar/account-settings/account-configuration/account-configuration.component.ts b/src/app/account/account-sidebar/account-settings/account-configuration/account-configuration.component.ts index ca21d530a0..0ec4be00d8 100644 --- a/src/app/account/account-sidebar/account-settings/account-configuration/account-configuration.component.ts +++ b/src/app/account/account-sidebar/account-settings/account-configuration/account-configuration.component.ts @@ -1,37 +1,32 @@ -import { - Component, - EventEmitter, - Input, - Output -} from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Configuration } from '../../../../shared/models/configuration.model'; import { EditAccountConfigurationComponent } from './edit-account-configuration.component'; import { MatDialog } from '@angular/material'; @Component({ selector: 'cs-account-configuration', - templateUrl: 'account-configuration.component.html' + templateUrl: 'account-configuration.component.html', }) export class AccountConfigurationComponent { - @Input() public configuration: Configuration; - @Output() public onConfigurationEdit: EventEmitter; + @Input() + public configuration: Configuration; + @Output() + public configurationEdited: EventEmitter; - constructor( - private dialog: MatDialog - ) { - this.onConfigurationEdit = new EventEmitter(); + constructor(private dialog: MatDialog) { + this.configurationEdited = new EventEmitter(); } public edit(): void { - this.dialog.open(EditAccountConfigurationComponent, { - width: '375px', - data: { - title: 'ACCOUNT_PAGE.CONFIGURATION.EDIT', - configuration: this.configuration - } - }) + this.dialog + .open(EditAccountConfigurationComponent, { + width: '375px', + data: { + title: 'ACCOUNT_PAGE.CONFIGURATION.EDIT', + configuration: this.configuration, + }, + }) .afterClosed() - .subscribe(configurationPair => this.onConfigurationEdit.emit(configurationPair)); + .subscribe(configurationPair => this.configurationEdited.emit(configurationPair)); } - } diff --git a/src/app/account/account-sidebar/account-settings/account-configuration/edit-account-configuration.component.ts b/src/app/account/account-sidebar/account-settings/account-configuration/edit-account-configuration.component.ts index a62b45691b..37909903d3 100644 --- a/src/app/account/account-sidebar/account-settings/account-configuration/edit-account-configuration.component.ts +++ b/src/app/account/account-sidebar/account-settings/account-configuration/edit-account-configuration.component.ts @@ -3,10 +3,9 @@ import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material'; @Component({ selector: 'cs-edit-account-configuration', - templateUrl: 'edit-account-configuration.component.html' + templateUrl: 'edit-account-configuration.component.html', }) export class EditAccountConfigurationComponent { - public name: string; public value: string; public title: string; @@ -17,13 +16,13 @@ export class EditAccountConfigurationComponent { ) { this.name = data.configuration.name; this.value = data.configuration.value; - this.title = data.title + this.title = data.title; } public onConfigurationUpdate(): void { const newConfiguration = { name: this.name, - value: this.value + value: this.value, }; this.dialogRef.close(newConfiguration); @@ -32,5 +31,4 @@ export class EditAccountConfigurationComponent { public close() { this.dialogRef.close(); } - } diff --git a/src/app/account/account-sidebar/account-settings/account-settings.component.html b/src/app/account/account-sidebar/account-settings/account-settings.component.html index bf292444ae..073df5c6cc 100644 --- a/src/app/account/account-sidebar/account-settings/account-settings.component.html +++ b/src/app/account/account-sidebar/account-settings/account-settings.component.html @@ -11,7 +11,7 @@

diff --git a/src/app/account/account-sidebar/account-settings/account-settings.component.ts b/src/app/account/account-sidebar/account-settings/account-settings.component.ts index 667701a091..188bb2e751 100644 --- a/src/app/account/account-sidebar/account-settings/account-settings.component.ts +++ b/src/app/account/account-sidebar/account-settings/account-settings.component.ts @@ -1,22 +1,19 @@ -import { - Component, - EventEmitter, - Input, - Output -} from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { Configuration } from '../../../shared/models/configuration.model'; import { Account } from '../../../shared/models/account.model'; @Component({ selector: 'cs-account-settings', - templateUrl: 'account-settings.component.html' + templateUrl: 'account-settings.component.html', }) export class AccountSettingsComponent { - @Input() public account: Account; - @Input() public configurations: Array; + @Input() + public account: Account; + @Input() + public configurations: Configuration[]; - @Output() public onConfigurationEdit = new EventEmitter(); - - constructor( ) { } + @Output() + public configurationEdited = new EventEmitter(); + constructor() {} } diff --git a/src/app/account/account-sidebar/account-sidebar.component.ts b/src/app/account/account-sidebar/account-sidebar.component.ts index 0eca5c5f56..5a7a95383d 100644 --- a/src/app/account/account-sidebar/account-sidebar.component.ts +++ b/src/app/account/account-sidebar/account-sidebar.component.ts @@ -8,11 +8,13 @@ import { isUserBelongsToAccount } from '../../shared/utils/account'; @Component({ selector: 'cs-account-sidebar', templateUrl: 'account-sidebar.component.html', - styleUrls: ['account-sidebar.component.scss'] + styleUrls: ['account-sidebar.component.scss'], }) export class AccountSidebarComponent { - @Input() public entity: Account; - @Output() public onAccountChanged = new EventEmitter(); + @Input() + public entity: Account; + @Output() + public accountChanged = new EventEmitter(); public get isSelf() { return isUserBelongsToAccount(this.authService.user, this.entity); @@ -21,14 +23,13 @@ export class AccountSidebarComponent { constructor( protected route: ActivatedRoute, protected router: Router, - protected authService: AuthService - ) { - } + protected authService: AuthService, + ) {} public tabIsActive(tabId: string) { const path = this.route.snapshot; const pathLastChild = path.firstChild.routeConfig.path; - return (tabId === pathLastChild); + return tabId === pathLastChild; } public isAdmin() { diff --git a/src/app/account/account-sidebar/account-statistic/account-statistics.component.ts b/src/app/account/account-sidebar/account-statistic/account-statistics.component.ts index 83a0a8b012..868954939d 100644 --- a/src/app/account/account-sidebar/account-statistic/account-statistics.component.ts +++ b/src/app/account/account-sidebar/account-statistic/account-statistics.component.ts @@ -7,11 +7,13 @@ import { ResourceType } from '../../../shared/models/resource-limit.model'; @Component({ selector: 'cs-account-statistics', - templateUrl: 'account-statistics.component.html' + templateUrl: 'account-statistics.component.html', }) export class AccountStatisticsComponent { - @Input() public stats: Array; - @Output() public statisticsUpdate = new EventEmitter(); + @Input() + public stats: ResourceCount[]; + @Output() + public statisticsUpdate = new EventEmitter(); public resourceLabels = { [ResourceType.Instance]: 'ACCOUNT_PAGE.CONFIGURATION.VM_COUNT', @@ -28,18 +30,17 @@ export class AccountStatisticsComponent { [ResourceType.SecondaryStorage]: 'ACCOUNT_PAGE.CONFIGURATION.SSTORAGE_COUNT', }; - constructor( - private dialogService: DialogService - ) { - } - + constructor(private dialogService: DialogService) {} public confirmUpdateStats() { - this.dialogService.confirm({ - message: 'ACCOUNT_PAGE.SIDEBAR.ARE_YOU_SURE_UPDATE_STATS' - }).pipe( - onErrorResumeNext(), - filter(res => Boolean(res))) + this.dialogService + .confirm({ + message: 'ACCOUNT_PAGE.SIDEBAR.ARE_YOU_SURE_UPDATE_STATS', + }) + .pipe( + onErrorResumeNext(), + filter(Boolean), + ) .subscribe(res => this.statisticsUpdate.emit(res)); } } diff --git a/src/app/account/account-sidebar/account-users/account-user-card.component.ts b/src/app/account/account-sidebar/account-users/account-user-card.component.ts index ee612c8332..6896b94581 100644 --- a/src/app/account/account-sidebar/account-users/account-user-card.component.ts +++ b/src/app/account/account-sidebar/account-users/account-user-card.component.ts @@ -3,9 +3,9 @@ import { AccountUser } from '../../../shared/models/account-user.model'; @Component({ selector: 'cs-account-user-card', - templateUrl: 'account-user-card.component.html' - + templateUrl: 'account-user-card.component.html', }) export class AccountUserCardComponent { - @Input() public user: AccountUser; + @Input() + public user: AccountUser; } diff --git a/src/app/account/account-sidebar/account-users/account-user-edit.component.ts b/src/app/account/account-sidebar/account-users/account-user-edit.component.ts index 6275f982c0..13cf305156 100644 --- a/src/app/account/account-sidebar/account-users/account-user-edit.component.ts +++ b/src/app/account/account-sidebar/account-users/account-user-edit.component.ts @@ -4,37 +4,38 @@ import { AccountUser, AccountUserForm } from '../../../shared/models/account-use @Component({ selector: 'cs-account-user-edit', - templateUrl: 'account-user-edit.component.html' + templateUrl: 'account-user-edit.component.html', }) export class AccountUserEditComponent implements OnInit { - @Input() public title: string; - @Input() public confirmButtonText: string; - @Input() public user: AccountUser; + @Input() + public title: string; + @Input() + public confirmButtonText: string; + @Input() + public user: AccountUser; - @Output() public updateUser = new EventEmitter(); + @Output() + public updateUser = new EventEmitter(); public userForm: FormGroup; public loading = false; public showPassword = true; - constructor( - private formBuilder: FormBuilder - ) { + constructor(private formBuilder: FormBuilder) { this.userForm = this.formBuilder.group({ - username: this.formBuilder.control('', [ Validators.required ]), - email: this.formBuilder.control('', [ Validators.required, Validators.email ]), - firstname: this.formBuilder.control('', [ Validators.required ]), - lastname: this.formBuilder.control('', [ Validators.required ]), + username: this.formBuilder.control('', [Validators.required]), + email: this.formBuilder.control('', [Validators.required, Validators.email]), + firstname: this.formBuilder.control('', [Validators.required]), + lastname: this.formBuilder.control('', [Validators.required]), timezone: this.formBuilder.control(null), }); - } ngOnInit() { if (this.user && this.user.id) { this.userForm.patchValue(this.user); } else { - this.userForm.addControl('password', this.formBuilder.control('', [ Validators.required ])); + this.userForm.addControl('password', this.formBuilder.control('', [Validators.required])); } } @@ -44,7 +45,7 @@ export class AccountUserEditComponent implements OnInit { } public prepareData(data: AccountUserForm): AccountUser { - const result = {}; + const result = {} as AccountUser; result.username = data.username; result.email = data.email; if (data.password) { @@ -55,6 +56,6 @@ export class AccountUserEditComponent implements OnInit { if (data.timezone) { result.timezone = data.timezone.geo; } - return result; + return result; } } diff --git a/src/app/account/account-sidebar/account-users/account-user-password.component.ts b/src/app/account/account-sidebar/account-users/account-user-password.component.ts index 086d2420b7..49712bcc38 100644 --- a/src/app/account/account-sidebar/account-users/account-user-password.component.ts +++ b/src/app/account/account-sidebar/account-users/account-user-password.component.ts @@ -5,7 +5,8 @@ import { Component, EventEmitter, Output } from '@angular/core'; templateUrl: 'account-user-password.component.html', }) export class AccountUserPasswordFormComponent { - @Output() public changePassword = new EventEmitter(); + @Output() + public changePassword = new EventEmitter(); public password: string; public confirmPassword: string; diff --git a/src/app/account/account-sidebar/account-users/account-users.component.html b/src/app/account/account-sidebar/account-users/account-users.component.html index c316ce91a2..be388f6be0 100644 --- a/src/app/account/account-sidebar/account-users/account-users.component.html +++ b/src/app/account/account-sidebar/account-users/account-users.component.html @@ -34,10 +34,10 @@ > diff --git a/src/app/account/account-sidebar/account-users/account-users.component.ts b/src/app/account/account-sidebar/account-users/account-users.component.ts index 4b354a98f6..18d0c14638 100644 --- a/src/app/account/account-sidebar/account-users/account-users.component.ts +++ b/src/app/account/account-sidebar/account-users/account-users.component.ts @@ -10,30 +10,36 @@ import { AccountUserPasswordFormContainerComponent } from '../../account-contain templateUrl: 'account-users.component.html', }) export class AccountUsersComponent { - @Input() public account: Account; - @Input() public isAdmin: boolean; - @Input() public currentUserId: string; + @Input() + public account: Account; + @Input() + public isAdmin: boolean; + @Input() + public currentUserId: string; - @Output() public onUserDelete = new EventEmitter(); - @Output() public onUserRegenerateKey = new EventEmitter(); - @Output() public onLoadUserKeys = new EventEmitter(); + @Output() + public userDeleted = new EventEmitter(); + @Output() + public userRegenerateKey = new EventEmitter(); + @Output() + public loadUserKeys = new EventEmitter(); public step: string; - public get sortedUsers(): Array { - return this.account && this.account.user ? [...this.account.user] - .sort((u1, u2) => u1.firstname.localeCompare(u2.firstname)) : []; + public get sortedUsers(): AccountUser[] { + return this.account && this.account.user + ? [...this.account.user].sort((u1, u2) => u1.firstname.localeCompare(u2.firstname)) + : []; } - constructor(private dialog: MatDialog) { - } + constructor(private dialog: MatDialog) {} public addUser() { this.openUserFormDialog(); } public deleteUser(user) { - this.onUserDelete.emit(user); + this.userDeleted.emit(user); } public editUser(user) { @@ -41,15 +47,16 @@ export class AccountUsersComponent { } public regenerateKeys(user) { - this.onUserRegenerateKey.emit(user); + this.userRegenerateKey.emit(user); this.setStep(user.id); } public onUserChangePassword(user) { - this.dialog.open(AccountUserPasswordFormContainerComponent, { - width: '375px', - data: { user } - }) + this.dialog + .open(AccountUserPasswordFormContainerComponent, { + width: '375px', + data: { user }, + }) .afterClosed() .subscribe(() => this.setStep(user.id)); } @@ -61,20 +68,21 @@ export class AccountUsersComponent { public openItem(user) { this.setStep(user.id); if (user.apikey && !user.secretkey) { - this.onLoadUserKeys.emit(user); + this.loadUserKeys.emit(user); } } private openUserFormDialog(user?: AccountUser) { - this.dialog.open(AccountUserEditContainerComponent, { - width: '375px', - data: { - title: !user ? 'ACCOUNT_PAGE.USER.CREATE_USER' : null, - confirmButtonText: !user ? 'COMMON.CREATE' : null, - account: this.account, - user - } - }) + this.dialog + .open(AccountUserEditContainerComponent, { + width: '375px', + data: { + user, + title: !user ? 'ACCOUNT_PAGE.USER.CREATE_USER' : null, + confirmButtonText: !user ? 'COMMON.CREATE' : null, + account: this.account, + }, + }) .afterClosed() .subscribe(updatedUser => { if (updatedUser) { diff --git a/src/app/account/account/account-item.component.spec.ts b/src/app/account/account/account-item.component.spec.ts index 3f99b123e5..f510ff8022 100644 --- a/src/app/account/account/account-item.component.spec.ts +++ b/src/app/account/account/account-item.component.spec.ts @@ -5,6 +5,7 @@ import { User } from '../../shared/models/user.model'; import { Account } from '../../shared/models'; class MockAuthService { + // tslint:disable-next-line:variable-name _user: User; get user() { @@ -21,7 +22,7 @@ describe('AccountItemComponent class only', () => { authServiceStub = new MockAuthService(); TestBed.configureTestingModule({ - providers: [{provide: AuthService, useValue: authServiceStub}] + providers: [{ provide: AuthService, useValue: authServiceStub }], }); authService = TestBed.get(AuthService); @@ -35,12 +36,12 @@ describe('AccountItemComponent class only', () => { comp.item = { id: '2', name: account1 } as Account; spy.and.returnValue({ - account: account1 + account: account1, }); expect(comp.isSelf).toBe(true); spy.and.returnValue({ - account: account2 + account: account2, }); expect(comp.isSelf).toBe(false); }); diff --git a/src/app/account/account/account-item.component.ts b/src/app/account/account/account-item.component.ts index a58f22dbb2..2a6845c84c 100644 --- a/src/app/account/account/account-item.component.ts +++ b/src/app/account/account/account-item.component.ts @@ -17,8 +17,7 @@ export class AccountItemComponent { return isUserBelongsToAccount(this.authService.user, this.item); } - constructor(protected authService: AuthService) { - } + constructor(protected authService: AuthService) {} public handleClick(e: MouseEvent): void { e.stopPropagation(); diff --git a/src/app/account/account/card-item/account-card-item.component.ts b/src/app/account/account/card-item/account-card-item.component.ts index 4ed0469687..a97e2ffe2d 100644 --- a/src/app/account/account/card-item/account-card-item.component.ts +++ b/src/app/account/account/card-item/account-card-item.component.ts @@ -1,10 +1,4 @@ -import { - Component, - EventEmitter, - Input, - Output, - ViewChild -} from '@angular/core'; +import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { MatMenuTrigger } from '@angular/material'; import { Account } from '../../../shared/models/account.model'; import { AccountItemComponent } from '../account-item.component'; @@ -13,13 +7,18 @@ import { AuthService } from '../../../shared/services/auth.service'; @Component({ selector: 'cs-account-card-item', templateUrl: 'account-card-item.component.html', - styleUrls: ['account-card-item.component.scss'] + styleUrls: ['account-card-item.component.scss'], }) export class AccountCardItemComponent extends AccountItemComponent { - @Input() public item: Account; - @Input() public isSelected: (account) => boolean; - @Output() public onClick = new EventEmitter(); - @ViewChild(MatMenuTrigger) public matMenuTrigger: MatMenuTrigger; + @Input() + public item: Account; + @Input() + public isSelected: (account) => boolean; + // tslint:disable-next-line:no-output-on-prefix + @Output() + public onClick = new EventEmitter(); + @ViewChild(MatMenuTrigger) + public matMenuTrigger: MatMenuTrigger; constructor(protected authService: AuthService) { super(authService); diff --git a/src/app/account/account/row-item/account-row-item.component.ts b/src/app/account/account/row-item/account-row-item.component.ts index 9adfdb7a26..70b4646548 100644 --- a/src/app/account/account/row-item/account-row-item.component.ts +++ b/src/app/account/account/row-item/account-row-item.component.ts @@ -1,10 +1,4 @@ -import { - Component, - EventEmitter, - Input, - Output, - ViewChild -} from '@angular/core'; +import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { MatMenuTrigger } from '@angular/material'; import { Account } from '../../../shared/models/account.model'; import { AccountItemComponent } from '../account-item.component'; @@ -13,13 +7,18 @@ import { AuthService } from '../../../shared/services/auth.service'; @Component({ selector: 'cs-account-row-item', templateUrl: 'account-row-item.component.html', - styleUrls: ['account-row-item.component.scss'] + styleUrls: ['account-row-item.component.scss'], }) export class AccountRowItemComponent extends AccountItemComponent { - @Input() public item: Account; - @Input() public isSelected: (account) => boolean; - @Output() public onClick = new EventEmitter(); - @ViewChild(MatMenuTrigger) public matMenuTrigger: MatMenuTrigger; + @Input() + public item: Account; + @Input() + public isSelected: (account) => boolean; + // tslint:disable-next-line:no-output-on-prefix + @Output() + public onClick = new EventEmitter(); + @ViewChild(MatMenuTrigger) + public matMenuTrigger: MatMenuTrigger; constructor(protected authService: AuthService) { super(authService); diff --git a/src/app/account/accounts.module.ts b/src/app/account/accounts.module.ts index 7a965df7b2..86d05b702f 100644 --- a/src/app/account/accounts.module.ts +++ b/src/app/account/accounts.module.ts @@ -71,7 +71,7 @@ import { AccountCreationComponent } from './creation-form/account-creation.compo RolesEffects, ConfigurationEffects, ResourceLimitsEffects, - ResourceCountsEffects + ResourceCountsEffects, ]), ], declarations: [ @@ -101,17 +101,14 @@ import { AccountCreationComponent } from './creation-form/account-creation.compo AccountUserEditComponent, AccountUserEditContainerComponent, AccountUserPasswordFormComponent, - AccountUserPasswordFormContainerComponent + AccountUserPasswordFormContainerComponent, ], entryComponents: [ EditAccountConfigurationComponent, AccountCreationContainerComponent, AccountUserEditContainerComponent, - AccountUserPasswordFormContainerComponent + AccountUserPasswordFormContainerComponent, ], - exports: [ - AccountPageComponent - ] + exports: [AccountPageComponent], }) -export class AccountModule { -} +export class AccountModule {} diff --git a/src/app/account/accounts.routing.ts b/src/app/account/accounts.routing.ts index 0e8d84df3e..47675f9185 100644 --- a/src/app/account/accounts.routing.ts +++ b/src/app/account/accounts.routing.ts @@ -14,7 +14,7 @@ export const accountsRoutes: Routes = [ children: [ { path: 'create', - component: AccountCreationComponent + component: AccountCreationComponent, }, { path: ':id', @@ -24,18 +24,18 @@ export const accountsRoutes: Routes = [ { path: '', redirectTo: 'account', - pathMatch: 'full' + pathMatch: 'full', }, { path: 'account', - component: AccountDetailsContainerComponent + component: AccountDetailsContainerComponent, }, { path: 'users', - component: AccountUsersContainerComponent - } - ] - } - ] - } + component: AccountUsersContainerComponent, + }, + ], + }, + ], + }, ]; diff --git a/src/app/account/creation-form/account-creation-dialog.component.spec.ts b/src/app/account/creation-form/account-creation-dialog.component.spec.ts index dbf99f9d63..0f6dc688fb 100644 --- a/src/app/account/creation-form/account-creation-dialog.component.spec.ts +++ b/src/app/account/creation-form/account-creation-dialog.component.spec.ts @@ -1,17 +1,17 @@ -import { Component, ViewChild, Input, forwardRef } from '@angular/core'; -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Component, forwardRef, Input, ViewChild } from '@angular/core'; +import { async, TestBed } from '@angular/core/testing'; import { + ControlValueAccessor, FormsModule, - ReactiveFormsModule, NG_VALUE_ACCESSOR, - ControlValueAccessor + ReactiveFormsModule, } from '@angular/forms'; import { - MatTooltipModule, - MatInputModule, MatButtonModule, + MatIconModule, + MatInputModule, MatSelectModule, - MatIconModule + MatTooltipModule, } from '@angular/material'; import { TranslateService } from '@ngx-translate/core'; import { AccountCreationDialogComponent } from './account-creation-dialog.component'; @@ -24,23 +24,23 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { By } from '@angular/platform-browser'; import { AccountData } from '../../shared/models/account.model'; - @Component({ selector: 'cs-test', template: ` - ` + `, }) class TestComponent { - @ViewChild(AccountCreationDialogComponent) public accountComponent: AccountCreationDialogComponent; + @ViewChild(AccountCreationDialogComponent) + public accountComponent: AccountCreationDialogComponent; public domains: Domain[]; public roles: Role[]; - public accountCreate(account: AccountData) { } + public accountCreate(account: AccountData) {} } @Component({ @@ -55,9 +55,9 @@ class TestComponent { { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TestTimeZoneComponent), - multi: true - } - ] + multi: true, + }, + ], }) class TestTimeZoneComponent implements ControlValueAccessor { public writeValue(value: any) {} @@ -67,10 +67,11 @@ class TestTimeZoneComponent implements ControlValueAccessor { @Component({ selector: 'cs-overlay-loading', - template: `` + template: ``, }) class TestOverlayComponent { - @Input() public active: boolean; + @Input() + public active: boolean; } describe('AccountCreationDialogComponent', () => { @@ -81,7 +82,6 @@ describe('AccountCreationDialogComponent', () => { return { f, testComponent }; } - beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ @@ -99,18 +99,12 @@ describe('AccountCreationDialogComponent', () => { TestTimeZoneComponent, TestOverlayComponent, TestComponent, - MockTranslatePipe + MockTranslatePipe, ], - providers: [ - { provide: TranslateService, useClass: MockTranslateService } - ] - }) - .compileComponents(); - - + providers: [{ provide: TranslateService, useClass: MockTranslateService }], + }).compileComponents(); })); - it('should check the submit button is disabled', async(async () => { const { f, testComponent } = createTestComponent(); @@ -141,7 +135,6 @@ describe('AccountCreationDialogComponent', () => { testComponent.accountComponent.accountForm.controls['roleid'].setValue('1'); f.detectChanges(); expect(buttons[1].nativeElement.disabled).toBeFalsy(); - })); it('should check the email field', async(async () => { @@ -161,7 +154,6 @@ describe('AccountCreationDialogComponent', () => { testComponent.accountComponent.accountForm.controls['roleid'].setValue('1'); f.detectChanges(); expect(buttons[1].nativeElement.disabled).toBeTruthy(); - })); it('should submit form', async(async () => { @@ -189,9 +181,8 @@ describe('AccountCreationDialogComponent', () => { firstname: 'name', lastname: 'name', domainid: '1', - roleid: '1' + roleid: '1', }); - })); it('should submit full form', async(async () => { @@ -208,7 +199,7 @@ describe('AccountCreationDialogComponent', () => { testComponent.accountComponent.accountForm.controls['lastname'].setValue('name'); testComponent.accountComponent.accountForm.controls['domainid'].setValue('1'); testComponent.accountComponent.accountForm.controls['roleid'].setValue('1'); - testComponent.accountComponent.accountForm.controls['timezone'].setValue({ geo: 'GEO'}); + testComponent.accountComponent.accountForm.controls['timezone'].setValue({ geo: 'GEO' }); testComponent.accountComponent.accountForm.controls['networkdomain'].setValue('domain'); f.detectChanges(); expect(buttons[1].nativeElement.disabled).toBeFalsy(); @@ -222,9 +213,7 @@ describe('AccountCreationDialogComponent', () => { domainid: '1', roleid: '1', timezone: 'GEO', - networkdomain: 'domain' + networkdomain: 'domain', }); - })); - }); diff --git a/src/app/account/creation-form/account-creation-dialog.component.ts b/src/app/account/creation-form/account-creation-dialog.component.ts index 2e651044d6..e38bdcc93c 100644 --- a/src/app/account/creation-form/account-creation-dialog.component.ts +++ b/src/app/account/creation-form/account-creation-dialog.component.ts @@ -6,28 +6,30 @@ import { Role } from '../../shared/models/role.model'; @Component({ selector: 'cs-account-creation-dialog', - templateUrl: 'account-creation-dialog.component.html' + templateUrl: 'account-creation-dialog.component.html', }) export class AccountCreationDialogComponent { public showPassword = true; public accountForm: FormGroup; - @Input() public isLoading: boolean; - @Input() public domains: Domain[]; - @Input() public roles: Role[]; - @Output() public onAccountCreate = new EventEmitter(); + @Input() + public isLoading: boolean; + @Input() + public domains: Domain[]; + @Input() + public roles: Role[]; + @Output() + public accountCreated = new EventEmitter(); - constructor( - private formBuilder: FormBuilder - ) { + constructor(private formBuilder: FormBuilder) { this.accountForm = this.formBuilder.group({ - username: this.formBuilder.control('', [ Validators.required ]), - email: this.formBuilder.control('', [ Validators.required, Validators.email ]), - password: this.formBuilder.control('', [ Validators.required ]), - firstname: this.formBuilder.control('', [ Validators.required ]), - lastname: this.formBuilder.control('', [ Validators.required ]), - domainid: this.formBuilder.control('', [ Validators.required ]), - roleid: this.formBuilder.control('', [ Validators.required ]), + username: this.formBuilder.control('', [Validators.required]), + email: this.formBuilder.control('', [Validators.required, Validators.email]), + password: this.formBuilder.control('', [Validators.required]), + firstname: this.formBuilder.control('', [Validators.required]), + lastname: this.formBuilder.control('', [Validators.required]), + domainid: this.formBuilder.control('', [Validators.required]), + roleid: this.formBuilder.control('', [Validators.required]), timezone: this.formBuilder.control(null), networkdomain: this.formBuilder.control(null), }); @@ -35,7 +37,7 @@ export class AccountCreationDialogComponent { public onSubmit(): void { const accountCreationParams = this.prepareData(this.accountForm.value); - this.onAccountCreate.emit(accountCreationParams); + this.accountCreated.emit(accountCreationParams); } public prepareData(data: AccountForm): AccountData { diff --git a/src/app/account/creation-form/account-creation.component.ts b/src/app/account/creation-form/account-creation.component.ts index eabec86095..1fec7e69f4 100644 --- a/src/app/account/creation-form/account-creation.component.ts +++ b/src/app/account/creation-form/account-creation.component.ts @@ -1,29 +1,25 @@ import { Component } from '@angular/core'; -import { - ActivatedRoute, - Router -} from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { MatDialog } from '@angular/material'; import { AccountCreationContainerComponent } from '../account-container/account-creation.container'; @Component({ selector: 'cs-account-creation', - template: '' + template: '', }) export class AccountCreationComponent { - constructor( - private dialog: MatDialog, - private router: Router, - private route: ActivatedRoute - ) { - this.dialog.open(AccountCreationContainerComponent, { - disableClose: true, - width: '360px', - }) + constructor(private dialog: MatDialog, private router: Router, private route: ActivatedRoute) { + this.dialog + .open(AccountCreationContainerComponent, { + disableClose: true, + width: '360px', + }) .afterClosed() - .subscribe(() => this.router.navigate(['../'], { - queryParamsHandling: 'preserve', - relativeTo: this.route - })); + .subscribe(() => + this.router.navigate(['../'], { + queryParamsHandling: 'preserve', + relativeTo: this.route, + }), + ); } } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 2fcf922768..931a2578d0 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -18,20 +18,19 @@ import { LoginGuard } from './shared/services/login-guard.service'; import { LoginComponent } from './auth/login.component'; import { vmLogsRoutes } from './vm-logs/vm-logs.routing'; - const routes: Routes = [ { path: 'login', component: LoginComponent, - canActivate: [LoginGuard] + canActivate: [LoginGuard], }, { path: 'logout', - component: LogoutComponent + component: LogoutComponent, }, { path: 'reload', - component: ReloadComponent + component: ReloadComponent, }, { path: '', @@ -46,30 +45,29 @@ const routes: Routes = [ { path: 'events', component: EventListContainerComponent, - canActivate: [AuthGuard] + canActivate: [AuthGuard], }, ...sshRoutes, ...vmLogsRoutes, { path: 'settings', component: SettingsComponent, - canActivate: [AuthGuard] + canActivate: [AuthGuard], }, { path: '**', - redirectTo: 'instances' - } + redirectTo: 'instances', + }, ], }, { path: '**', - redirectTo: '' - } + redirectTo: '', + }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], - exports: [RouterModule] + exports: [RouterModule], }) -export class AppRoutingModule { -} +export class AppRoutingModule {} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index a1e4802010..5a21a43891 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -9,14 +9,13 @@ import { CacheService } from './shared/services/cache.service'; import { MemoryStorageService } from './shared/services/memory-storage.service'; import { SessionStorageService } from './shared/services/session-storage.service'; import { StyleService } from './shared/services/style.service'; -import { UserService } from './shared/services/user.service'; import { DateTimeFormatterService } from './shared/services/date-time-formatter.service'; import { State, UserTagsSelectors } from './root-store'; @Component({ selector: 'cs-app', templateUrl: './app.component.html', - styleUrls: ['./app.component.scss'] + styleUrls: ['./app.component.scss'], }) export class AppComponent implements OnInit { constructor( @@ -27,10 +26,8 @@ export class AppComponent implements OnInit { private sessionStorage: SessionStorageService, private memoryStorage: MemoryStorageService, private styleService: StyleService, - private userService: UserService, - private store: Store - ) { - } + private store: Store, + ) {} public ngOnInit(): void { this.auth.loggedIn.subscribe(() => { @@ -42,25 +39,29 @@ export class AppComponent implements OnInit { this.configureInterface(); } - private configureInterface() { - this.store.pipe(select(UserTagsSelectors.getInterfaceLanguage)) + this.store + .pipe(select(UserTagsSelectors.getInterfaceLanguage)) .subscribe(language => this.translateService.use(language)); - this.store.pipe(select(UserTagsSelectors.getTimeFormat)) + this.store + .pipe(select(UserTagsSelectors.getTimeFormat)) .subscribe(timeFormat => this.dateTimeFormatterService.updateFormatters(timeFormat)); - this.store.pipe(select(UserTagsSelectors.getTheme)) + this.store + .pipe(select(UserTagsSelectors.getTheme)) .subscribe(themeName => this.styleService.useTheme(themeName)); - this.translateService.onLangChange.pipe( - switchMap(() => this.store.pipe( - select(UserTagsSelectors.getTimeFormat), - first(), - ))) - .subscribe((format) => - this.dateTimeFormatterService.updateFormatters(format) - ); + this.translateService.onLangChange + .pipe( + switchMap(() => + this.store.pipe( + select(UserTagsSelectors.getTimeFormat), + first(), + ), + ), + ) + .subscribe(format => this.dateTimeFormatterService.updateFormatters(format)); } private storageReset() { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b35c981862..0cb3cfa1cd 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -8,6 +8,7 @@ import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { DragulaModule } from 'ng2-dragula'; import { NgIdleKeepaliveModule } from '@ng-idle/keepalive'; import { select, Store } from '@ngrx/store'; +import { createInputTransfer, removeNgStyles } from '@angularclass/hmr'; import { filter, first, take } from 'rxjs/operators'; import { AccountModule } from './account/accounts.module'; @@ -36,31 +37,45 @@ import { BaseHttpInterceptor } from './shared/services/base-http-interceptor'; import { createInputTransfer, removeNgStyles } from '@angularclass/hmr'; import { VmLogsModule } from './vm-logs/vm-logs.module'; +// tslint:disable-next-line:function-name export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader { return new TranslateHttpLoader(http, './i18n/', '.json'); } +// tslint:disable-next-line:function-name export function InitAppFactory( auth: AuthService, http: HttpClient, translateService: TranslateService, - store: Store + store: Store, ) { - return () => store.pipe( - select(configSelectors.isLoaded), - filter(Boolean), - first() - ).toPromise() - .then(() => store.pipe( - select(configSelectors.get('defaultInterfaceLanguage')), - first() - ).subscribe(lang => translateService.setDefaultLang(lang)) - ) - .then(() => auth.initUser()) - .then(() => store.pipe( - select(configSelectors.getDefaultUserTags), - first() - ).subscribe(tags => store.dispatch(new UserTagsActions.SetDefaultUserTagsAtStartup({ tags })))); + return () => + store + .pipe( + select(configSelectors.isLoaded), + filter(Boolean), + first(), + ) + .toPromise() + .then(() => + store + .pipe( + select(configSelectors.get('defaultInterfaceLanguage')), + first(), + ) + .subscribe(lang => translateService.setDefaultLang(lang)), + ) + .then(() => auth.initUser()) + .then(() => + store + .pipe( + select(configSelectors.getDefaultUserTags), + first(), + ) + .subscribe(tags => + store.dispatch(new UserTagsActions.SetDefaultUserTagsAtStartup({ tags })), + ), + ); } @NgModule({ @@ -95,37 +110,28 @@ export function InitAppFactory( loader: { provide: TranslateLoader, useFactory: HttpLoaderFactory, - deps: [HttpClient] - } + deps: [HttpClient], + }, }), ], - declarations: [ - AppComponent, - HomeComponent - ], + declarations: [AppComponent, HomeComponent], providers: [ { provide: APP_INITIALIZER, useFactory: InitAppFactory, - deps: [ - AuthService, - HttpClient, - TranslateService, - Store - ], - multi: true + deps: [AuthService, HttpClient, TranslateService, Store], + multi: true, }, { provide: HTTP_INTERCEPTORS, useClass: BaseHttpInterceptor, - multi: true - } + multi: true, + }, ], - bootstrap: [AppComponent] + bootstrap: [AppComponent], }) export class AppModule { - constructor(public appRef: ApplicationRef, private store: Store) { - } + constructor(public appRef: ApplicationRef, private store: Store) {} hmrOnInit(store) { if (!store || !store.rootState) { @@ -135,7 +141,7 @@ export class AppModule { if (store.rootState) { this.store.dispatch({ type: 'SET_ROOT_STATE', - payload: store.rootState + payload: store.rootState, }); } if ('restoreInputValues' in store) { @@ -147,7 +153,7 @@ export class AppModule { hmrOnDestroy(store) { const cmpLocation = this.appRef.components.map(cmp => cmp.location.nativeElement); - this.store.pipe(take(1)).subscribe(s => store.rootState = s); + this.store.pipe(take(1)).subscribe(s => (store.rootState = s)); store.disposeOldHosts = this.createNewHosts(cmpLocation); store.restoreInputValues = createInputTransfer(); removeNgStyles(); @@ -159,7 +165,7 @@ export class AppModule { } createNewHosts(cmps) { - const components = Array.prototype.map.call(cmps, function (componentNode) { + const components = Array.prototype.map.call(cmps, componentNode => { const newNode = document.createElement(componentNode.tagName); const currentDisplay = newNode.style.display; newNode.style.display = 'none'; @@ -170,16 +176,13 @@ export class AppModule { newNode.style.display = currentDisplay; try { parentNode.removeChild(componentNode); - } catch (e) { - } + } catch (e) {} }; - } else { - return function () { - }; // make it callable } + return function() {}; // make it callable }); return function removeOldHosts() { - components.forEach(function (removeOldHost) { + components.forEach(removeOldHost => { return removeOldHost(); }); }; diff --git a/src/app/auth/auth.module.ts b/src/app/auth/auth.module.ts index 33fd187cb7..47cd524b61 100644 --- a/src/app/auth/auth.module.ts +++ b/src/app/auth/auth.module.ts @@ -10,16 +10,7 @@ import { LoginComponent } from './login.component'; import { LogoutComponent } from './logout.component'; @NgModule({ - imports: [ - CommonModule, - SharedModule, - MaterialModule, - EffectsModule.forFeature([AuthEffects]) - ], - declarations: [ - LoginComponent, - LogoutComponent - ] + imports: [CommonModule, SharedModule, MaterialModule, EffectsModule.forFeature([AuthEffects])], + declarations: [LoginComponent, LogoutComponent], }) -export class AuthModule { -} +export class AuthModule {} diff --git a/src/app/auth/login.component.ts b/src/app/auth/login.component.ts index 952dab4c1d..5001111f0c 100644 --- a/src/app/auth/login.component.ts +++ b/src/app/auth/login.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit, ViewChild } from '@angular/core'; import { AbstractControl } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { Store } from '@ngrx/store'; +import { select, Store } from '@ngrx/store'; import { first } from 'rxjs/operators'; import { AuthService } from '../shared/services/auth.service'; @@ -12,11 +12,13 @@ import { configSelectors, State } from '../root-store'; @Component({ selector: 'cs-login', templateUrl: './login.component.html', - styleUrls: ['login.component.scss'] + styleUrls: ['login.component.scss'], }) export class LoginComponent implements OnInit { - @ViewChild('user') public usernameField; - @ViewChild('pass') public passwordField; + @ViewChild('user') + public usernameField; + @ViewChild('pass') + public passwordField; public username = ''; public password = ''; @@ -33,18 +35,20 @@ export class LoginComponent implements OnInit { private router: Router, private storage: LocalStorageService, private store: Store, - ) { - } + ) {} public ngOnInit(): void { const value = this.storage.read(this.key); this.showDomain = value === 'true'; const domainFromQueryParams = this.route.snapshot.queryParams['domain']; - this.store.select(configSelectors.get('defaultDomain')).pipe( - first() - ).subscribe(domainFromConfig => { - this.domain = domainFromQueryParams || domainFromConfig || ''; - }); + this.store + .pipe( + select(configSelectors.get('defaultDomain')), + first(), + ) + .subscribe(domainFromConfig => { + this.domain = domainFromQueryParams || domainFromConfig || ''; + }); this.loading = false; } @@ -81,9 +85,7 @@ export class LoginComponent implements OnInit { const { queryParams } = this.route.snapshot; const next = - queryParams['next'] && - queryParams['next'] !== '/login' && - queryParams['next'] !== 'login' + queryParams['next'] && queryParams['next'] !== '/login' && queryParams['next'] !== 'login' ? queryParams['next'] : ''; @@ -91,9 +93,11 @@ export class LoginComponent implements OnInit { } private handleError(error: any): void { - this.notification.open({ - translationToken: error.message, - interpolateParams: error.params - }).subscribe(); + this.notification + .open({ + translationToken: error.message, + interpolateParams: error.params, + }) + .subscribe(); } } diff --git a/src/app/auth/logout.component.ts b/src/app/auth/logout.component.ts index a87a557eb1..92d2876468 100644 --- a/src/app/auth/logout.component.ts +++ b/src/app/auth/logout.component.ts @@ -10,7 +10,7 @@ import { LogoutComplete } from './store/auth.actions'; @Component({ selector: 'cs-logout', - template: '
' + template: '
', }) export class LogoutComponent implements OnInit { constructor( @@ -19,16 +19,14 @@ export class LogoutComponent implements OnInit { private dialog: MatDialog, private router: Router, private store: Store, - private routerUtilsService: RouterUtilsService + private routerUtilsService: RouterUtilsService, ) {} public ngOnInit(): void { this.authService.logout().subscribe(() => { this.store.dispatch(new LogoutComplete()); const next = this.activatedRoute.snapshot.queryParams['next']; - const redirectionParams = next - ? this.routerUtilsService.getRedirectionQueryParams(next) - : {}; + const redirectionParams = next ? this.routerUtilsService.getRedirectionQueryParams(next) : {}; this.router.navigate(['/login'], redirectionParams); this.dialog.closeAll(); }); diff --git a/src/app/auth/store/auth.actions.ts b/src/app/auth/store/auth.actions.ts index 3e9d53c2f8..b4efc4bc14 100644 --- a/src/app/auth/store/auth.actions.ts +++ b/src/app/auth/store/auth.actions.ts @@ -2,7 +2,7 @@ import { Action } from '@ngrx/store'; export enum AuthActionTypes { IdleLogout = '[Idle monitor] Logout', - LogoutComplete = '[Auth API] Logout complete' + LogoutComplete = '[Auth API] Logout complete', } export class IdleLogout implements Action { @@ -13,6 +13,4 @@ export class LogoutComplete implements Action { readonly type = AuthActionTypes.LogoutComplete; } -export type AuthActionsUnion = - | IdleLogout - | LogoutComplete; +export type AuthActionsUnion = IdleLogout | LogoutComplete; diff --git a/src/app/auth/store/auth.effects.ts b/src/app/auth/store/auth.effects.ts index eed3b5d64f..26537cde19 100644 --- a/src/app/auth/store/auth.effects.ts +++ b/src/app/auth/store/auth.effects.ts @@ -7,7 +7,6 @@ import { mergeMap, tap, withLatestFrom } from 'rxjs/operators'; import { AuthActionTypes, IdleLogout, LogoutComplete } from './auth.actions'; import { RouterUtilsService } from '../../shared/services/router-utils.service'; -import { AuthService } from '../../shared/services/auth.service'; import { configSelectors, IdleMonitorActions, State, UserTagsActions } from '../../root-store/'; @Injectable() @@ -15,7 +14,9 @@ export class AuthEffects { @Effect({ dispatch: false }) idleLogout$: Observable = this.actions$.pipe( ofType(AuthActionTypes.IdleLogout), - tap(() => this.router.navigate(['/logout'], this.routerUtilsService.getRedirectionQueryParams())), + tap(() => + this.router.navigate(['/logout'], this.routerUtilsService.getRedirectionQueryParams()), + ), ); @Effect() @@ -24,16 +25,14 @@ export class AuthEffects { withLatestFrom(this.store.pipe(select(configSelectors.getDefaultUserTags))), mergeMap(([action, tags]) => [ new IdleMonitorActions.StopIdleMonitor(), - new UserTagsActions.SetDefaultUserTagsDueToLogout({ tags }) - ]) + new UserTagsActions.SetDefaultUserTagsDueToLogout({ tags }), + ]), ); constructor( private actions$: Actions, - private authService: AuthService, private router: Router, private routerUtilsService: RouterUtilsService, - private store: Store - ) { - } + private store: Store, + ) {} } diff --git a/src/app/core/components/index.ts b/src/app/core/components/index.ts deleted file mode 100644 index 5790187317..0000000000 --- a/src/app/core/components/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './sidenav/sidenav.component'; diff --git a/src/app/core/components/sidenav/_sidenav-theme.scss b/src/app/core/components/sidenav/_sidenav-theme.scss deleted file mode 100644 index c2da1736c1..0000000000 --- a/src/app/core/components/sidenav/_sidenav-theme.scss +++ /dev/null @@ -1,14 +0,0 @@ -@mixin app-sidebar-theme($theme) { - $primary: map-get($theme, primary); - $background: map-get($theme, background); - - cs-sidenav { - background: mat-color($primary); - color: mat-color($background, card); - - .navigation-link:hover, - .link-active { - background-color: #757575 !important; - } - } -} diff --git a/src/app/core/components/sidenav/sidenav-animations.ts b/src/app/core/components/sidenav/sidenav-animations.ts deleted file mode 100644 index 6dd9fe56cb..0000000000 --- a/src/app/core/components/sidenav/sidenav-animations.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { animate, AnimationTriggerMetadata, state, style, transition, trigger } from '@angular/animations'; - -const animationDuration = 150; - -/** - * When drag handle is attached to the DOM, it slides from off screen to the right. - * When it is detached, it slides back - * @type {AnimationTriggerMetadata} - */ -export const transformHandle: AnimationTriggerMetadata = trigger('handleSlide', [ - state('in', style({ transform: 'translateX(0)' })), - transition( - 'void => *', - [style({ transform: 'translateX(-60px)' }), animate(animationDuration)] - ), - transition( - 'in => void', - [animate(animationDuration, style({ transform: 'translateX(-30px)' }))] - ) -]); - -/** - * When drag mode is enabled, sidebar links shift to the right - * to make space for drag handles. - * When disabled, they slide back to the original place. - * - * Note that in both situations different lists are visible, that is why - * both transitions are from void state. - * @type {AnimationTriggerMetadata} - */ -export const transformLinks: AnimationTriggerMetadata = trigger('linkSlide', [ - state('out', style({ transform: 'translateX(60px)' })), - transition( - 'void => in', - [style({ transform: 'translateX(40px)' }), animate(animationDuration)] - ), - transition( - 'void => out', - [style({ transform: 'translateX(25px)' }), animate(animationDuration)] - ) -]); diff --git a/src/app/core/components/sidenav/sidenav-routes.ts b/src/app/core/components/sidenav/sidenav-routes.ts deleted file mode 100644 index 315cd227b5..0000000000 --- a/src/app/core/components/sidenav/sidenav-routes.ts +++ /dev/null @@ -1,92 +0,0 @@ -export interface NavigationItem { - id: string; - visible: boolean; -} - -export interface SidenavRoute extends NavigationItem { - path: string; - text: string; - icon: string; -} - -export const sidenavRoutes: Array = [ - { - path: '/instances', - text: 'NAVIGATION_SIDEBAR.VMS', - icon: 'mdi-cloud', - id: 'VMS', - visible: true - }, - { - path: '/storage', - text: 'NAVIGATION_SIDEBAR.STORAGE', - icon: 'mdi-server', - id: 'VOLUMES', - visible: true - }, - { - path: '/templates', - text: 'NAVIGATION_SIDEBAR.IMAGES', - icon: 'mdi-disc', - id: 'TEMPLATES', - visible: true - }, { - path: '/snapshots', - text: 'NAVIGATION_SIDEBAR.SNAPSHOTS', - icon: 'mdi-camera', - id: 'SNAPSHOTS', - visible: true - }, - { - path: '/security-group', - text: 'NAVIGATION_SIDEBAR.FIREWALL_TEMPLATES', - icon: 'mdi-security', - id: 'SGS', - visible: true - }, - { - path: '/events', - text: 'NAVIGATION_SIDEBAR.ACTIVITY_LOG', - icon: 'mdi-calendar-text', - id: 'EVENTS', - visible: true - }, - { - path: '/ssh-keys', - text: 'NAVIGATION_SIDEBAR.SSH_KEYS', - icon: 'mdi-key', - id: 'SSH', - visible: true - }, - { - path: '/accounts', - text: 'NAVIGATION_SIDEBAR.ACCOUNTS', - icon: 'mdi-account', - id: 'ACCOUNTS', - visible: true - }, - { - path: '/logs', - text: 'NAVIGATION_SIDEBAR.LOGS', - icon: 'mdi-text', - id: 'LOGS', - visible: true - }, - { - path: '/settings', - text: 'NAVIGATION_SIDEBAR.SETTINGS', - icon: 'mdi-settings', - id: 'SETTINGS', - visible: true - } -]; - -export const nonDraggableRoutes: Array = [ - { - path: '/logout', - text: 'NAVIGATION_SIDEBAR.LOGOUT', - icon: 'mdi-exit-to-app', - id: 'LOGOUT', - visible: true - } -]; diff --git a/src/app/core/components/sidenav/sidenav.component.html b/src/app/core/components/sidenav/sidenav.component.html deleted file mode 100644 index 59a962964e..0000000000 --- a/src/app/core/components/sidenav/sidenav.component.html +++ /dev/null @@ -1,103 +0,0 @@ -
- - -
- -
- -
- - -
-
- - - -
-
- {{ 'NAVIGATION_SIDEBAR.LICENSE.LICENSE_1' | translate: { year: currentYear } }} - - {{ 'NAVIGATION_SIDEBAR.LICENSE.LICENSE_2' | translate }} - -
- {{ 'NAVIGATION_SIDEBAR.LICENSE.LICENSE_3' | translate }} -
-
diff --git a/src/app/core/components/sidenav/sidenav.component.scss b/src/app/core/components/sidenav/sidenav.component.scss deleted file mode 100644 index ff2a5f2e37..0000000000 --- a/src/app/core/components/sidenav/sidenav.component.scss +++ /dev/null @@ -1,157 +0,0 @@ -:host { - display: flex; - flex-direction: column; - min-height: 100vh; -} - -.navigation { - flex-direction: column; - align-items: stretch; - padding-top: 16px; - - &-link { - display: block; - padding: 12px 30px !important; - flex-shrink: 0; - color: #757575; - text-decoration: none; - margin: 0; - font-size: 13px; - font-weight: 400; - line-height: 24px; - letter-spacing: 0; - opacity: .87; - - mat-icon { - margin-right: 10px; - } - - & > div { - display: inline-block; - } - } - - .drag { - position: relative; - } -} - -.link-element { - display: inline-block; - border-color: inherit; - vertical-align: middle; - &-edit { - transform: translateX(60px); - } -} - -a { - color: inherit !important; - text-decoration: none; -} - -.title-header { - line-height: 1; - padding-bottom: 15px; - text-align: center; - font-size: 13px; - word-break: break-all; -} - -.top-buttons { - flex-shrink: 0; - display: flex; - justify-content: space-between; - - mat-icon { - color: white !important; - } - .hide-button { - margin: 15px 0 0 15px; - } - - .lock-button { - margin: 15px 15px 0 0; - } -} - - -.license { - width: 100%; - font-size: 12px; - padding: 5px; - line-height: 1; - text-align: center; - flex: 1; - align-self: flex-end; - - a { - text-decoration: underline; - } - - &-container { - display: flex; - flex: 1; - flex-direction: row; - min-height: 40px; - } -} - -div.link-container { - color: inherit !important; - background-color: inherit !important; -} - -.logo { - margin: 20px; - height: 47px; - max-width: 100%; - background-repeat: no-repeat; - background-size: contain; -} - -.handle { - cursor: move !important; - cursor: grabbing !important; - display: inline-block; - position: absolute; - top: 0; - bottom: 0; - left: 15px; - z-index: 500; - - &::before { - content: ""; - position: absolute; - top: 0; - bottom: 0; - margin: auto; - background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iIAogICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9ICJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjbGFzcz0iZHJhZy1oYW5kbGUiIGFyaWEtaGlkZGVuPSJ0cnVlIiB3aWR0aD0iMTYiIGhlaWdodD0iMTUiIHZlcnNpb249IjEuMSIgdmlld0JveD0iMCAwIDE2IDE1Ij48cGF0aCBkPSJNMTIsNFY1SDRWNGg4Wk00LDhoOFY3SDRWOFptMCwzaDhWMTBINHYxWiI+PC9wYXRoPjwvc3ZnPgo=); - background-size: 28px 28px; - width: 28px; - height: 28px; - line-height: 48px; - display: inline-block; - vertical-align: middle; - } -} - -mat-checkbox { - display: inline-block; - position: absolute; - left: 55px; - z-index: 50; - top: 50%; - transform: translateY(-50%); -} - -.gu-mirror.navigation-link { - display: block; - padding: 16px 40px; - background: rgba(0, 0, 0, 0.2) !important; -} - -div.drag.link-container { - cursor: move !important; - cursor: grabbing !important; -} diff --git a/src/app/core/components/sidenav/sidenav.component.ts b/src/app/core/components/sidenav/sidenav.component.ts deleted file mode 100644 index b555249307..0000000000 --- a/src/app/core/components/sidenav/sidenav.component.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; -import { Router } from '@angular/router'; -import { select, Store } from '@ngrx/store'; -import { filter, first, takeUntil, tap } from 'rxjs/operators'; -import { DragulaService } from 'ng2-dragula'; -import * as cloneDeep from 'lodash/cloneDeep'; - -import { RouterUtilsService } from '../../../shared/services/router-utils.service'; -import { WithUnsubscribe } from '../../../utils/mixins/with-unsubscribe'; -import { transformHandle, transformLinks } from './sidenav-animations'; -import { NavigationItem, nonDraggableRoutes, SidenavRoute, sidenavRoutes } from './sidenav-routes'; -import { SidenavRouteId } from '../../config'; -import { SidenavConfigElement } from '../../../shared/models/config'; -import { configSelectors, layoutActions, State, UserTagsActions, UserTagsSelectors } from '../../../root-store'; - - -@Component({ - selector: 'cs-sidenav', - templateUrl: './sidenav.component.html', - styleUrls: ['./sidenav.component.scss'], - animations: [transformHandle, transformLinks] -}) -export class SidenavComponent extends WithUnsubscribe() implements OnInit, OnDestroy { - @ViewChild('navigationBar') public navigationBar: ElementRef; - @Input() public open: boolean; - @Input() public title: string; - @Input() public allowConfiguring: boolean; - public imgUrl = 'url(img/cloudstack_logo_light.png)'; - - public routes: Array = cloneDeep(sidenavRoutes); - public nonDraggableRoutes = nonDraggableRoutes; - - public navigationLoaded = false; - public updatingOrder = false; - public dragulaContainerName = 'sidebar-bag'; - - private _editing = false; - private hasChanges = false; - - constructor( - private dragula: DragulaService, - private routerUtilsService: RouterUtilsService, - private router: Router, - private store: Store - ) { - super(); - } - - public ngOnInit() { - this.setUpRoutes(); - this.setUpDragula(); - this.initNavigationOrder(); - } - - public ngOnDestroy(): void { - super.ngOnDestroy(); - this.dragula.destroy(this.dragulaContainerName); - } - - public get editing(): boolean { - return this._editing; - } - - public linkClick(routerLink: string): void { - if (routerLink === this.routerUtilsService.getRouteWithoutQueryParams()) { - this.router.navigate(['reload'], { - queryParamsHandling: 'preserve' - }); - } - } - - public get currentYear(): string { - return new Date().getFullYear().toString(); - } - - public closeSidenav(): void { - this.store.dispatch(new layoutActions.CloseSidenav()); - } - - public toggleEditing(): void { - if (this.updatingOrder) { - return; - } - if (this.editing && this.hasChanges) { - this.hasChanges = false; - this.updatingOrder = true; - - const menuState = this.stringifyMenuState(this.routes); - this.store.dispatch(new UserTagsActions.UpdateNavigationOrder({ value: menuState })); - this.updatingOrder = false; - } - this.toggleState(); - } - - public handleRouteChecked() { - this.hasChanges = true; - } - - public isAlwaysVisible(route: SidenavRoute): boolean { - const visibleRouteIds: SidenavRouteId[] = ['VMS']; - return visibleRouteIds.findIndex((id => id === route.id)) >= 0; - } - - private setUpDragula(): void { - this.dragula.setOptions(this.dragulaContainerName, { - moves: () => this.editing - }); - - this.dragula.dropModel.pipe( - takeUntil(this.unsubscribe$)) - .subscribe(() => (this.hasChanges = true)); - } - - private initNavigationOrder() { - if (this.allowConfiguring) { - this.store.pipe( - select(UserTagsSelectors.getNavigationOrder), - tap(() => this.navigationLoaded = true), - filter(Boolean)) - .subscribe(tag => { - const order = this.parseMenuState(tag); - - if (this.validateNavigationOrder(order)) { - const predicate = this.navigationPredicate(order); - this.routes.sort(predicate); - this.routes.forEach((route, i) => route.visible = order[i].visible); - } - }); - } else { - this.navigationLoaded = true; - } - } - - private toggleState(): void { - this._editing = !this._editing; - } - - public setUpRoutes() { - if (!this.allowConfiguring) { - return; - } - - this.store.pipe( - select(configSelectors.get('configureSidenav')), - first() - ).subscribe((sidenavConfiguration: SidenavConfigElement[]) => { - const routesMap = this.getRoutesMap(); - this.routes = sidenavConfiguration.map(confElement => { - const route = routesMap[confElement.id]; - route.visible = confElement.visible; - return route; - }); - }); - } - - private getRoutesMap(): { [id: string]: SidenavRoute } { - return this.routes.reduce((map, route) => { - map[route.id] = route; - return map; - }, {}); - } - - private validateNavigationOrder(order: NavigationItem[]): boolean { - if (order.length !== this.routes.length) { - return false; - } - - return order.every(el => - el.visible != null && el.id != null && !!this.routes.find(route => route.id === el.id)); - } - - private navigationPredicate(order: NavigationItem[]) { - return (a: NavigationItem, b: NavigationItem) => - order.findIndex(el => el.id === a.id) - order.findIndex(el => el.id === b.id); - } - - private stringifyMenuState(routes: NavigationItem[]): string { - return routes - .map(el => `${el.id}:${+el.visible}`) - .join(';'); - } - - private parseMenuState(menuStateString: string): NavigationItem[] { - return menuStateString - .split(';') - .filter(Boolean) - .map((menuElement: string) => { - const [id, visible] = menuElement.split(':'); - return { - id, - visible: visible === '1' - } - }); - } -} diff --git a/src/app/core/config/config-validation.service.spec.ts b/src/app/core/config/config-validation.service.spec.ts index c73dcb27ed..ece9c07e4a 100644 --- a/src/app/core/config/config-validation.service.spec.ts +++ b/src/app/core/config/config-validation.service.spec.ts @@ -19,7 +19,7 @@ describe('ConfigValidationService', () => { } }); - it('should merge user\'s value if it is correct (only if property has a validator) to resulted config', () => { + it("should merge user's value if it is correct (only if property has a validator) to resulted config", () => { const config: Partial = { defaultDomain: 'develop', }; @@ -27,7 +27,7 @@ describe('ConfigValidationService', () => { expect(conf.defaultDomain).toBe('develop'); }); - it('should not merge user\'s value if it is incorrect (only if property has a validator) to resulted config', () => { + it("should not merge user's value if it is incorrect (only if property has a validator) to resulted config", () => { const config = { defaultInterfaceLanguage: 'fr', defaultFirstDayOfWeek: 'monday', @@ -47,36 +47,35 @@ describe('ConfigValidationService', () => { it('should not add unknown properties', () => { const config = { - noSuchProperty: true + noSuchProperty: true, }; const conf = configValidationService.validate(config); expect(conf['noSuchProperty']).toBeUndefined(); }); it('should use default value if user do not provide a value', () => { - const config = { - }; + const config = {}; const conf = configValidationService.validate(config); expect(conf.defaultInterfaceLanguage).toBe(defaultConfig.defaultInterfaceLanguage); }); - it('should log a warning message to the console if a user\'s key is unknown', () => { + it("should log a warning message to the console if a user's key is unknown", () => { const config = { - noSuchProperty: true + noSuchProperty: true, }; configValidationService.validate(config); expect(console.warn).toHaveBeenCalledTimes(1); }); - it('should log a warning message to the console if a user\'s value is incorrect', () => { + it("should log a warning message to the console if a user's value is incorrect", () => { const config = { - defaultFirstDayOfWeek: 'monday' + defaultFirstDayOfWeek: 'monday', }; configValidationService.validate(config); expect(console.warn).toHaveBeenCalledTimes(1); }); - it('should log a warning message to the console if a user\'s config is incorrect', () => { + it("should log a warning message to the console if a user's config is incorrect", () => { const config = 'defaultFirstDayOfWeek: "monday"'; configValidationService.validate(config); expect(console.warn).toHaveBeenCalledTimes(1); diff --git a/src/app/core/config/config-validation.service.ts b/src/app/core/config/config-validation.service.ts index c9bf491a87..992b35d740 100644 --- a/src/app/core/config/config-validation.service.ts +++ b/src/app/core/config/config-validation.service.ts @@ -1,6 +1,5 @@ import { Injectable } from '@angular/core'; import * as AjvCore from 'ajv'; -import { Ajv } from 'ajv'; import * as AjvUniqueItemProperties from 'ajv-keywords/keywords/uniqueItemProperties'; import * as AjvErrors from 'ajv-errors'; import * as omit from 'lodash/omit'; @@ -12,12 +11,11 @@ import * as validationSchemes from './validation-schemes'; enum ErrorType { InvalidConfig, InvalidKey, - InvalidValue + InvalidValue, } abstract class ValidationError { - protected constructor(readonly type: ErrorType, readonly message: string) { - } + protected constructor(readonly type: ErrorType, readonly message: string) {} public getErrorText(): string { return `Configuration warning:\n${this.message}`; @@ -51,29 +49,30 @@ class InvalidValueError extends ValidationError { } } -type ValidationScheme = { - readonly [P in keyof Partial]: object; -} +type ValidationScheme = { readonly [P in keyof Partial]: object }; @Injectable() export class ConfigValidationService { - private readonly schemeValidator: Ajv; + private readonly schemeValidator: AjvCore.Ajv; private readonly schemeMap: ValidationScheme = { defaultDomain: validationSchemes.defaultDomain, + apiDocLink: validationSchemes.apiDocLink, sessionRefreshInterval: validationSchemes.sessionRefreshInterval, extensions: validationSchemes.extensions, + vmColors: validationSchemes.vmColors, defaultFirstDayOfWeek: validationSchemes.defaultFirstDayOfWeek, defaultInterfaceLanguage: validationSchemes.defaultInterfaceLanguage, defaultTimeFormat: validationSchemes.defaultTimeFormat, defaultTheme: validationSchemes.defaultTheme, defaultComputeOffering: validationSchemes.defaultComputeOffering, sessionTimeout: validationSchemes.sessionTimeout, - configureSidenav: validationSchemes.configureSidenav, customComputeOfferingParameters: validationSchemes.customComputeOfferingParameters, serviceOfferingAvailability: validationSchemes.serviceOfferingAvailability, imageGroups: validationSchemes.imageGroups, computeOfferingClasses: validationSchemes.computeOfferingClasses, - defaultSecurityGroupName: validationSchemes.defaultSecurityGroupName + defaultSecurityGroupName: validationSchemes.defaultSecurityGroupName, + offeringCompatibilityPolicy: validationSchemes.offeringCompatibilityPolicy, + securityGroupTemplates: validationSchemes.securityGroupTemplates, }; constructor() { @@ -117,13 +116,7 @@ export class ConfigValidationService { private isValidValue(key: string, value: any) { const scheme = this.schemeMap[key]; - - // Condition needed until all schemes not implemented - if (scheme) { - return this.schemeValidator.validate(scheme, value); - } else { - return true; - } + return this.schemeValidator.validate(scheme, value); } private getFixedConfig(userConf: object, errors: ValidationError[]): Partial { diff --git a/src/app/core/config/default-configuration.ts b/src/app/core/config/default-configuration.ts index 948325c032..3145174770 100644 --- a/src/app/core/config/default-configuration.ts +++ b/src/app/core/config/default-configuration.ts @@ -26,7 +26,7 @@ const COLORS = [ { value: '#FFFDE7' }, { value: '#FFF3E0' }, { value: '#FFFFFF' }, - { value: '#ECEFF1' } + { value: '#ECEFF1' }, ]; export const customizableProperties: Readonly = { @@ -38,7 +38,7 @@ export const customizableProperties: Readonly = { apiDocLink: 'https://cloudstack.apache.org/api/apidocs-4.11/', extensions: { webShell: false, - pulse: false + pulse: false, }, /* * Virtual machines settings @@ -50,7 +50,7 @@ export const customizableProperties: Readonly = { securityGroupTemplates: [], defaultSecurityGroupName: { en: 'default', - ru: 'default' + ru: 'default', }, /* * Images settings @@ -88,7 +88,7 @@ export const customizableProperties: Readonly = { offeringCompatibilityPolicy: {}, computeOfferingClasses: [], serviceOfferingAvailability: { - 'filterOfferings': false + filterOfferings: false, }, }; @@ -102,33 +102,33 @@ export const nonCustomizableProperties: Readonly = { // The application will ask him about autosave passwords and set the value based on the user's choice. savePasswordForAllVMs: null, lastVMId: 0, - isSidenavVisible: true, showSystemTags: false, - // Should be empty string. Use configureSidenav instead - navigationOrder: '', /* * Offerings */ customComputeOfferingHardwareValues: { cpunumber: 1, cpuspeed: 1000, - memory: 512 + memory: 512, }, defaultCustomComputeOfferingRestrictions: { cpunumber: { min: 1, - max: Number.POSITIVE_INFINITY + max: Number.POSITIVE_INFINITY, }, cpuspeed: { min: 1000, - max: Number.POSITIVE_INFINITY + max: Number.POSITIVE_INFINITY, }, memory: { min: 512, - max: Number.POSITIVE_INFINITY - } + max: Number.POSITIVE_INFINITY, + }, }, - keyboardLayoutForVms: 'us' + keyboardLayoutForVms: 'us', }; -export const defaultConfig: Readonly = {...customizableProperties, ...nonCustomizableProperties}; +export const defaultConfig: Readonly = { + ...customizableProperties, + ...nonCustomizableProperties, +}; diff --git a/src/app/core/config/validation-schemes/api-doc-link.scheme.json b/src/app/core/config/validation-schemes/api-doc-link.scheme.json new file mode 100644 index 0000000000..290e21a746 --- /dev/null +++ b/src/app/core/config/validation-schemes/api-doc-link.scheme.json @@ -0,0 +1,9 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "apiDocLink", + "type": "string", + "pattern": "^(?:(?:(?:https?|ftp):)?\\/\\/)(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z0-9\\u00a1-\\uffff][a-z0-9\\u00a1-\\uffff_-]{0,62})?[a-z0-9\\u00a1-\\uffff]\\.)+(?:[a-z\\u00a1-\\uffff]{2,}\\.?))(?::\\d{2,5})?(?:[/?#]\\S*)?$", + "errorMessage": { + "pattern": "should be a URL" + } +} diff --git a/src/app/core/config/validation-schemes/configure-sidenav.scheme.json b/src/app/core/config/validation-schemes/configure-sidenav.scheme.json deleted file mode 100644 index 79475e0fe2..0000000000 --- a/src/app/core/config/validation-schemes/configure-sidenav.scheme.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "configureSidenav", - "type": "array", - "items": { - "oneOf": [ - { "$ref": "#/definitions/VMSidenavElement" }, - { "$ref": "#/definitions/SidenavElementExceptVM" } - ] - }, - "minItems": 9, - "maxItems": 9, - "uniqueItemProperties": ["id"], - "definitions": { - "SidenavElementExceptVM": { - "type": "object", - "required": ["id", "visible"], - "additionalProperties": false, - "properties": { - "id": { - "type": "string", - "enum": [ - "VOLUMES", - "TEMPLATES", - "SNAPSHOTS", - "SGS", - "EVENTS", - "SSH", - "ACCOUNTS", - "SETTINGS" - ] - }, - "visible": { - "type": "boolean" - } - } - }, - "VMSidenavElement": { - "type": "object", - "required": [ - "id" - ], - "additionalProperties": false, - "properties": { - "id": { - "type": "string", - "const": "VMS" - }, - "visible": { - "type": "boolean" - } - } - } - }, - "errorMessage": { - "uniqueItemProperties": "should NOT contain objects with the same id", - "minItems": "should contain all menu items", - "maxItems": "should contain all menu items" - } -} diff --git a/src/app/core/config/validation-schemes/custom-compute-offering-parameters.scheme.json b/src/app/core/config/validation-schemes/custom-compute-offering-parameters.scheme.json index f7b9c168d0..c71a546c44 100644 --- a/src/app/core/config/validation-schemes/custom-compute-offering-parameters.scheme.json +++ b/src/app/core/config/validation-schemes/custom-compute-offering-parameters.scheme.json @@ -5,12 +5,7 @@ "items": { "type": "object", "additionalProperties": false, - "required": [ - "offeringId", - "cpunumber", - "cpuspeed", - "memory" - ], + "required": ["offeringId", "cpunumber", "cpuspeed", "memory"], "properties": { "offeringId": { "type": "string" @@ -27,6 +22,9 @@ } }, "uniqueItemProperties": ["offeringId"], + "errorMessage": { + "uniqueItemProperties": "should NOT contain objects with the same offeringId" + }, "definitions": { "HardwareParameterLimits": { "type": "object", diff --git a/src/app/core/config/validation-schemes/index.ts b/src/app/core/config/validation-schemes/index.ts index ee0abb3269..1ed946f4ac 100644 --- a/src/app/core/config/validation-schemes/index.ts +++ b/src/app/core/config/validation-schemes/index.ts @@ -1,18 +1,24 @@ import * as sessionRefreshInterval from './session-refresh-interval.scheme.json'; import * as defaultDomain from './default-domain.scheme.json'; +import * as apiDocLink from './api-doc-link.scheme.json'; import * as extensions from './extensions.scheme.json'; +import * as vmColors from './vm-colors.scheme.json'; + import * as defaultFirstDayOfWeek from './default-first-day-of-week.scheme.json'; import * as defaultInterfaceLanguage from './default-interface-language.scheme.json'; import * as defaultTimeFormat from './default-time-format.scheme.json'; import * as defaultTheme from './default-theme.scheme.json'; import * as sessionTimeout from './session-timeout.scheme.json'; -import * as configureSidenav from './configure-sidenav.scheme.json'; import * as defaultComputeOffering from './default-compute-offering.scheme.json'; import * as computeOfferingClasses from './compute-offering-classes.scheme.json'; import * as serviceOfferingAvailability from './service-offering-availability.scheme.json'; import * as customComputeOfferingParameters from './custom-compute-offering-parameters.scheme.json'; +import * as offeringCompatibilityPolicy from './offering-compatibility-policy.scheme.json'; + +import * as securityGroupTemplates from './security-group-templates.scheme.json'; + import * as imageGroups from './image-groups.scheme.json'; import * as defaultSecurityGroupName from './default-security-group-name.scheme.json'; @@ -20,22 +26,26 @@ export { // General defaultDomain, sessionRefreshInterval, + apiDocLink, extensions, + // Virtual machines settings + vmColors, // User app settings defaultFirstDayOfWeek, defaultInterfaceLanguage, defaultTimeFormat, defaultTheme, sessionTimeout, - // Menu setting - configureSidenav, // Service offering setting customComputeOfferingParameters, defaultComputeOffering, serviceOfferingAvailability, computeOfferingClasses, + offeringCompatibilityPolicy, + // Security groups + securityGroupTemplates, // Images settings imageGroups, // Firewall (Security groups) settings - defaultSecurityGroupName -} + defaultSecurityGroupName, +}; diff --git a/src/app/core/config/validation-schemes/offering-compatibility-policy.scheme.json b/src/app/core/config/validation-schemes/offering-compatibility-policy.scheme.json new file mode 100644 index 0000000000..04d6d4fbc9 --- /dev/null +++ b/src/app/core/config/validation-schemes/offering-compatibility-policy.scheme.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "offeringCompatibilityPolicy", + "type": "object", + "additionalProperties": false, + "properties": { + "offeringChangePolicy": { + "type": "string", + "enum": ["contains-all", "exactly-match", "no-restrictions"] + }, + "offeringChangePolicyIgnoreTags": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string" + } + } + } +} diff --git a/src/app/core/config/validation-schemes/security-group-templates.scheme.json b/src/app/core/config/validation-schemes/security-group-templates.scheme.json new file mode 100644 index 0000000000..058713f5cb --- /dev/null +++ b/src/app/core/config/validation-schemes/security-group-templates.scheme.json @@ -0,0 +1,112 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "securityGroupTemplates", + "definitions": { + "TcpUdpPort": { + "type": "integer", + "minimum": 0, + "maximum": 65535 + }, + "TcpUdpRule": { + "type": "object", + "required": ["ruleid", "protocol", "startport", "endport", "cidr"], + "additionalProperties": false, + "properties": { + "ruleid": { + "type": "string", + "minLength": 1 + }, + "protocol": { + "type": "string", + "enum": ["tcp", "udp"] + }, + "startport": { + "$ref": "#/definitions/TcpUdpPort" + }, + "endport": { + "$ref": "#/definitions/TcpUdpPort" + }, + "cidr": { + "type": "string" + } + } + }, + "IcmpRule": { + "type": "object", + "required": ["ruleid", "protocol", "icmpcode", "icmptype", "cidr"], + "additionalProperties": false, + "properties": { + "ruleid": { + "type": "string", + "minLength": 1 + }, + "protocol": { + "type": "string", + "const": "icmp" + }, + "icmpcode": { + "type": "integer", + "minimum": -1, + "maximum": 255 + }, + "icmptype": { + "type": "integer", + "minimum": -1, + "maximum": 255 + }, + "cidr": { + "type": "string" + } + } + }, + "RulesArray": { + "type": "array", + "uniqueItemProperties": ["ruleid"], + "errorMessage": { + "uniqueItemProperties": "should NOT contain objects with the same ruleId" + }, + "items": { + "anyOf": [ + { + "$ref": "#/definitions/TcpUdpRule" + }, + { + "$ref": "#/definitions/IcmpRule" + } + ] + } + } + }, + "type": "array", + "uniqueItemProperties": ["id"], + "errorMessage": { + "uniqueItemProperties": "should NOT contain objects with the same id" + }, + "items": { + "type": "object", + "required": ["id", "name", "description", "preselected", "ingressrule", "egressrule"], + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "minLength": 1 + }, + "name": { + "type": "string", + "minLength": 1 + }, + "description": { + "type": "string" + }, + "preselected": { + "type": "boolean" + }, + "ingressrule": { + "$ref": "#/definitions/RulesArray" + }, + "egressrule": { + "$ref": "#/definitions/RulesArray" + } + } + } +} diff --git a/src/app/core/config/validation-schemes/vm-colors.scheme.json b/src/app/core/config/validation-schemes/vm-colors.scheme.json new file mode 100644 index 0000000000..f29f9bc5f5 --- /dev/null +++ b/src/app/core/config/validation-schemes/vm-colors.scheme.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "vmColors", + "type": "array", + "uniqueItems": true, + "items": { + "type": "object", + "required": ["value"], + "additionalProperties": false, + "properties": { + "value": { + "type": "string", + "pattern": "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$" + } + } + } +} diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index a6d6047b4e..df13ae2d47 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -1,40 +1,34 @@ import { NgModule, Optional, SkipSelf } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; -import { DragulaModule } from 'ng2-dragula'; -import { MaterialModule } from '../material/material.module'; -import { SharedModule } from '../shared/shared.module'; - -import { SidenavComponent } from './components'; -import { SnackBarService } from './services'; import { ConfigValidationService } from './config'; - -const COMPONENTS = [ - SidenavComponent -]; - -const SERVICES = [ - ConfigValidationService, - SnackBarService -]; +import { SharedModule } from '../shared/shared.module'; +import { MaterialModule } from '../material/material.module'; +import { + AppNavComponent, + LicenseComponent, + MenuHeaderComponent, + SectionNavComponent, +} from './nav-menu/components/'; +import { SnackBarService, NavMenuService } from './services'; @NgModule({ - imports: [ - CommonModule, - SharedModule, - MaterialModule, - DragulaModule, - RouterModule - ], - exports: COMPONENTS, - declarations: COMPONENTS, - providers: SERVICES, + imports: [CommonModule, SharedModule, MaterialModule, RouterModule], + exports: [AppNavComponent, SectionNavComponent], + declarations: [AppNavComponent, SectionNavComponent, LicenseComponent, MenuHeaderComponent], + providers: [ConfigValidationService, SnackBarService, NavMenuService], }) export class CoreModule { - constructor(@Optional() @SkipSelf() parentModule: CoreModule) { + constructor( + @Optional() + @SkipSelf() + parentModule: CoreModule, + ) { if (parentModule) { - throw new Error(`CoreModule has already been loaded. Import CoreModule in the AppModule only.`); + throw new Error( + `CoreModule has already been loaded. Import CoreModule in the AppModule only.`, + ); } } } diff --git a/src/app/core/nav-menu/components/app-nav/app-nav.component.html b/src/app/core/nav-menu/components/app-nav/app-nav.component.html new file mode 100644 index 0000000000..91e579b89b --- /dev/null +++ b/src/app/core/nav-menu/components/app-nav/app-nav.component.html @@ -0,0 +1,24 @@ + + + diff --git a/src/app/core/nav-menu/components/app-nav/app-nav.component.scss b/src/app/core/nav-menu/components/app-nav/app-nav.component.scss new file mode 100644 index 0000000000..57a5a0f839 --- /dev/null +++ b/src/app/core/nav-menu/components/app-nav/app-nav.component.scss @@ -0,0 +1,28 @@ +@import '../../../../../style/_variables'; + +:host { + display: flex; + flex-direction: column; + justify-content: space-between; + min-width: $nav-menu-min-width; + height: 100%; +} + +.nav-routes { + margin-bottom: 15px; + font-size: 13px; + display: flex; + flex-direction: column; + + .nav-link { + padding: 12px 25px; + display: flex; + align-items: center; + color: inherit; + text-decoration: none; + + mat-icon { + margin-right: 10px; + } + } +} diff --git a/src/app/core/nav-menu/components/app-nav/app-nav.component.ts b/src/app/core/nav-menu/components/app-nav/app-nav.component.ts new file mode 100644 index 0000000000..f38f84cd14 --- /dev/null +++ b/src/app/core/nav-menu/components/app-nav/app-nav.component.ts @@ -0,0 +1,22 @@ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; + +import { Route } from '../../models'; + +@Component({ + selector: 'cs-app-nav', + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './app-nav.component.html', + styleUrls: ['./app-nav.component.scss'], +}) +export class AppNavComponent { + @Input() + public routes: Route[]; + @Input() + public currentRoute: Route; + @Output() + public menuButtonClicked = new EventEmitter(); + + public onMenuButtonClicked(): void { + this.menuButtonClicked.emit(); + } +} diff --git a/src/app/core/nav-menu/components/index.ts b/src/app/core/nav-menu/components/index.ts new file mode 100644 index 0000000000..0c26a309bd --- /dev/null +++ b/src/app/core/nav-menu/components/index.ts @@ -0,0 +1,4 @@ +export * from './app-nav/app-nav.component'; +export * from './license/license.component'; +export * from './menu-header/menu-header.component'; +export * from './section-nav/section-nav.component'; diff --git a/src/app/core/nav-menu/components/license/license.component.html b/src/app/core/nav-menu/components/license/license.component.html new file mode 100644 index 0000000000..35be19c49f --- /dev/null +++ b/src/app/core/nav-menu/components/license/license.component.html @@ -0,0 +1,7 @@ +
+ {{ 'NAVIGATION_SIDEBAR.LICENSE.LICENSE_1' | translate: { year: year } }} + + {{ 'NAVIGATION_SIDEBAR.LICENSE.LICENSE_2' | translate }} + +
{{ 'NAVIGATION_SIDEBAR.LICENSE.LICENSE_3' | translate }}
+
diff --git a/src/app/core/nav-menu/components/license/license.component.scss b/src/app/core/nav-menu/components/license/license.component.scss new file mode 100644 index 0000000000..b3ee44f665 --- /dev/null +++ b/src/app/core/nav-menu/components/license/license.component.scss @@ -0,0 +1,10 @@ +.license { + font-size: 12px; + padding: 5px; + line-height: 1; + text-align: center; +} + +a { + color: inherit; +} diff --git a/src/app/core/nav-menu/components/license/license.component.ts b/src/app/core/nav-menu/components/license/license.component.ts new file mode 100644 index 0000000000..aed5264d76 --- /dev/null +++ b/src/app/core/nav-menu/components/license/license.component.ts @@ -0,0 +1,15 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; + +@Component({ + selector: 'cs-license', + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './license.component.html', + styleUrls: ['./license.component.scss'], +}) +export class LicenseComponent { + public year: string; + + constructor() { + this.year = new Date().getFullYear().toString(); + } +} diff --git a/src/app/core/nav-menu/components/menu-header/menu-header.component.html b/src/app/core/nav-menu/components/menu-header/menu-header.component.html new file mode 100644 index 0000000000..870724167e --- /dev/null +++ b/src/app/core/nav-menu/components/menu-header/menu-header.component.html @@ -0,0 +1,12 @@ + diff --git a/src/app/core/nav-menu/components/menu-header/menu-header.component.scss b/src/app/core/nav-menu/components/menu-header/menu-header.component.scss new file mode 100644 index 0000000000..65cf8249c0 --- /dev/null +++ b/src/app/core/nav-menu/components/menu-header/menu-header.component.scss @@ -0,0 +1,30 @@ +:host { + display: block; +} + +.menu-header { + font-size: 13px; + padding: 16px; + display: flex; + flex-direction: column; + + .mdi-menu { + color: inherit !important; + } + + .logo { + width: 210px; + margin: 15px 0; + align-self: center; + } + + .account-info { + margin: 0 9px; + display: flex; + align-items: center; + + mat-icon { + margin-right: 10px; + } + } +} diff --git a/src/app/core/nav-menu/components/menu-header/menu-header.component.ts b/src/app/core/nav-menu/components/menu-header/menu-header.component.ts new file mode 100644 index 0000000000..c26bafb02c --- /dev/null +++ b/src/app/core/nav-menu/components/menu-header/menu-header.component.ts @@ -0,0 +1,18 @@ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; + +@Component({ + selector: 'cs-menu-header', + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './menu-header.component.html', + styleUrls: ['./menu-header.component.scss'], +}) +export class MenuHeaderComponent { + @Input() + public username: string; + @Output() + public buttonClicked = new EventEmitter(); + + public onButtonClicked(): void { + this.buttonClicked.emit(); + } +} diff --git a/src/app/core/nav-menu/components/section-nav/section-nav.component.html b/src/app/core/nav-menu/components/section-nav/section-nav.component.html new file mode 100644 index 0000000000..cca262ac5b --- /dev/null +++ b/src/app/core/nav-menu/components/section-nav/section-nav.component.html @@ -0,0 +1,18 @@ + + + diff --git a/src/app/core/nav-menu/components/section-nav/section-nav.component.scss b/src/app/core/nav-menu/components/section-nav/section-nav.component.scss new file mode 100644 index 0000000000..b2da3c0894 --- /dev/null +++ b/src/app/core/nav-menu/components/section-nav/section-nav.component.scss @@ -0,0 +1,27 @@ +@import '../../../../../style/_variables'; + +:host { + display: flex; + flex-direction: column; + justify-content: space-between; + min-width: $nav-menu-min-width; +} + +.nav-routes { + margin-bottom: 15px; + font-size: 13px; + display: flex; + flex-direction: column; + + .nav-link { + padding: 12px 25px; + display: flex; + align-items: center; + color: inherit; + text-decoration: none; + + mat-icon { + margin-right: 10px; + } + } +} diff --git a/src/app/core/nav-menu/components/section-nav/section-nav.component.ts b/src/app/core/nav-menu/components/section-nav/section-nav.component.ts new file mode 100644 index 0000000000..d7850ea7e6 --- /dev/null +++ b/src/app/core/nav-menu/components/section-nav/section-nav.component.ts @@ -0,0 +1,21 @@ +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; +import { Subroute } from '../../models'; + +@Component({ + selector: 'cs-section-nav', + changeDetection: ChangeDetectionStrategy.OnPush, + templateUrl: './section-nav.component.html', + styleUrls: ['./section-nav.component.scss'], +}) +export class SectionNavComponent { + @Input() + public subroutes: Subroute[]; + @Input() + public username: string; + @Output() + public menuButtonClicked = new EventEmitter(); + + public onMenuButtonClicked(): void { + this.menuButtonClicked.emit(); + } +} diff --git a/src/app/core/nav-menu/models/index.ts b/src/app/core/nav-menu/models/index.ts new file mode 100644 index 0000000000..bec1ff990f --- /dev/null +++ b/src/app/core/nav-menu/models/index.ts @@ -0,0 +1,2 @@ +export * from './route.interface'; +export * from './subroute.interface'; diff --git a/src/app/core/nav-menu/models/route.interface.ts b/src/app/core/nav-menu/models/route.interface.ts new file mode 100644 index 0000000000..3d7fcfb8df --- /dev/null +++ b/src/app/core/nav-menu/models/route.interface.ts @@ -0,0 +1,9 @@ +import { Subroute } from './subroute.interface'; + +export interface Route { + id: string; + text: string; + path: string; + icon: string; + subroutes: Subroute[]; +} diff --git a/src/app/core/nav-menu/models/subroute.interface.ts b/src/app/core/nav-menu/models/subroute.interface.ts new file mode 100644 index 0000000000..c5de48af38 --- /dev/null +++ b/src/app/core/nav-menu/models/subroute.interface.ts @@ -0,0 +1,6 @@ +export interface Subroute { + text: string; + path: string; + icon: string; + routeId: string; +} diff --git a/src/app/core/nav-menu/nav-menu-theme.scss b/src/app/core/nav-menu/nav-menu-theme.scss new file mode 100644 index 0000000000..6b2fffee47 --- /dev/null +++ b/src/app/core/nav-menu/nav-menu-theme.scss @@ -0,0 +1,18 @@ +@mixin nav-menu-theme($theme) { + $primary: map-get($theme, primary); + + cs-menu-header { + background-color: mat-color($primary, 800); + } + + cs-app-nav, + cs-section-nav { + color: mat-color($primary, '500-contrast'); + background-color: mat-color($primary); + + .nav-link:hover, + .link-active { + background-color: mat-color($primary, 300); + } + } +} diff --git a/src/app/core/nav-menu/nav-menu.service.spec.ts b/src/app/core/nav-menu/nav-menu.service.spec.ts new file mode 100644 index 0000000000..605d79fe63 --- /dev/null +++ b/src/app/core/nav-menu/nav-menu.service.spec.ts @@ -0,0 +1,72 @@ +import { TestBed } from '@angular/core/testing'; +import { Store } from '@ngrx/store'; + +import { NavMenuService } from './nav-menu.service'; +import { TestStore } from '../../../testutils/ngrx-test-store'; +import { appNavRoutes } from './routes'; + +describe('NavMenuService', () => { + let service: NavMenuService; + let store: TestStore; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [NavMenuService, { provide: Store, useClass: TestStore }], + }); + service = TestBed.get(NavMenuService); + store = TestBed.get(Store); + }); + + it('should return an array of routes', () => { + service.getRoutes().subscribe(routes => { + expect(routes).toBe(appNavRoutes); + }); + }); + + // it('should return the route regardless of the selected subroute', () => { + // const expectedRoute = appNavRoutes.find(route => route.id === 'virtual-machines'); + // const validUrls = [ + // '/templates?viewMode=Template', + // '/storage', + // '/instances', + // '/security-group?viewMode=templates&orphan=false', + // '/snapshots?date=2018-10-17' + // ]; + // const invalidUrls = [ + // '/accounts', + // '/events?date=2018-10-17' + // ]; + // validUrls.forEach(url => { + // store.setState(url); + // service.getCurrentRoute().subscribe(route => { + // expect(route.id).toBe(expectedRoute.id) + // }) + // }); + // + // invalidUrls.forEach(url => { + // store.setState(url); + // service.getCurrentRoute().subscribe(route => { + // expect(route.id).not.toBe(expectedRoute.id) + // }) + // }); + // }); + + it('should return subroutes of the virtual-machines route based on URL', () => { + const url = + '/templates/template/884adbea-ae67-4aba-86c0-c4f794c5343b/details?viewMode=Template'; + store.setState(url); + const expectedRoute = appNavRoutes.find(route => route.id === 'virtual-machines'); + service.getSubroutes().subscribe(subroutes => { + expect(subroutes).toBe(expectedRoute.subroutes); + }); + }); + + it('should return subroutes of the accounts route based on URL', () => { + const url = '/events?date=2018-10-10&levels=WARN'; + store.setState(url); + const expectedRoute = appNavRoutes.find(route => route.id === 'accounts'); + service.getSubroutes().subscribe(subroutes => { + expect(subroutes).toBe(expectedRoute.subroutes); + }); + }); +}); diff --git a/src/app/core/nav-menu/nav-menu.service.ts b/src/app/core/nav-menu/nav-menu.service.ts new file mode 100644 index 0000000000..51de1aaeb7 --- /dev/null +++ b/src/app/core/nav-menu/nav-menu.service.ts @@ -0,0 +1,57 @@ +import { Injectable } from '@angular/core'; +import { select, Store } from '@ngrx/store'; +import { filter, map, withLatestFrom } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import * as flatten from 'lodash/flatten'; + +import { Route, Subroute } from './models'; +import { appNavRoutes } from './routes'; +import { routerSelectors, State } from '../../root-store'; + +@Injectable() +export class NavMenuService { + constructor(private store: Store) {} + + public getRoutes(): Observable { + return of(appNavRoutes); + } + + public getSubroutes(): Observable { + return this.getCurrentRoute().pipe(map(route => route.subroutes)); + } + + public getCurrentRoute(): Observable { + return this.getCurrentSubroute().pipe( + filter(subroute => !!subroute), + withLatestFrom(this.getRoutes()), + map(([subroute, routes]) => routes.find(route => route.id === subroute.routeId)), + ); + } + + private getCurrentSubroute(): Observable { + return this.getCurrentSubroutePath().pipe( + withLatestFrom(this.getAllSubroutes()), + map(([path, subroutes]) => { + return subroutes.find(subroute => subroute.path === path); + }), + ); + } + + private getCurrentSubroutePath(): Observable { + return this.store.pipe( + select(routerSelectors.getUrl), + filter(url => url !== ''), + map(url => { + const matches = url.match(/^\/[A-Za-z-]*/); + return matches[0]; + }), + ); + } + + private getAllSubroutes(): Observable { + return this.getRoutes().pipe( + map(routes => routes.map(route => route.subroutes)), + map(flatten), + ); + } +} diff --git a/src/app/core/nav-menu/routes/accounts-subroutes.ts b/src/app/core/nav-menu/routes/accounts-subroutes.ts new file mode 100644 index 0000000000..d3d41ead56 --- /dev/null +++ b/src/app/core/nav-menu/routes/accounts-subroutes.ts @@ -0,0 +1,22 @@ +import { Subroute } from '../models'; + +export const accountsSubroutes: Subroute[] = [ + { + text: 'NAVIGATION_SIDEBAR.ACCOUNTS', + path: '/accounts', + icon: 'mdi-account-supervisor', + routeId: 'accounts', + }, + { + text: 'NAVIGATION_SIDEBAR.ACTIVITY_LOG', + path: '/events', + icon: 'mdi-calendar-text', + routeId: 'accounts', + }, + { + text: 'NAVIGATION_SIDEBAR.SETTINGS', + path: '/settings', + icon: 'mdi-settings', + routeId: 'accounts', + }, +]; diff --git a/src/app/core/nav-menu/routes/app-nav-routes.ts b/src/app/core/nav-menu/routes/app-nav-routes.ts new file mode 100644 index 0000000000..fe4dbdd9d1 --- /dev/null +++ b/src/app/core/nav-menu/routes/app-nav-routes.ts @@ -0,0 +1,20 @@ +import { Route } from '../models'; +import { virtualMachinesSubroutes } from './virtual-machines-subroutes'; +import { accountsSubroutes } from './accounts-subroutes'; + +export const appNavRoutes: Route[] = [ + { + id: 'virtual-machines', + text: 'NAVIGATION_SIDEBAR.VMS', + path: '/instances', + icon: 'mdi-cloud', + subroutes: virtualMachinesSubroutes, + }, + { + id: 'accounts', + text: 'NAVIGATION_SIDEBAR.ACCOUNTS', + path: '/accounts', + icon: 'mdi-account-supervisor', + subroutes: accountsSubroutes, + }, +]; diff --git a/src/app/core/nav-menu/routes/index.ts b/src/app/core/nav-menu/routes/index.ts new file mode 100644 index 0000000000..9694af0b50 --- /dev/null +++ b/src/app/core/nav-menu/routes/index.ts @@ -0,0 +1 @@ +export * from './app-nav-routes'; diff --git a/src/app/core/nav-menu/routes/virtual-machines-subroutes.ts b/src/app/core/nav-menu/routes/virtual-machines-subroutes.ts new file mode 100644 index 0000000000..00d16ed437 --- /dev/null +++ b/src/app/core/nav-menu/routes/virtual-machines-subroutes.ts @@ -0,0 +1,40 @@ +import { Subroute } from '../models'; + +export const virtualMachinesSubroutes: Subroute[] = [ + { + text: 'NAVIGATION_SIDEBAR.VMS', + path: '/instances', + icon: 'mdi-cloud', + routeId: 'virtual-machines', + }, + { + text: 'NAVIGATION_SIDEBAR.STORAGE', + path: '/storage', + icon: 'mdi-server', + routeId: 'virtual-machines', + }, + { + text: 'NAVIGATION_SIDEBAR.IMAGES', + path: '/templates', + icon: 'mdi-disc', + routeId: 'virtual-machines', + }, + { + text: 'NAVIGATION_SIDEBAR.SNAPSHOTS', + path: '/snapshots', + icon: 'mdi-camera', + routeId: 'virtual-machines', + }, + { + text: 'NAVIGATION_SIDEBAR.FIREWALL_TEMPLATES', + path: '/security-group', + icon: 'mdi-security', + routeId: 'virtual-machines', + }, + { + text: 'NAVIGATION_SIDEBAR.SSH_KEYS', + path: '/ssh-keys', + icon: 'mdi-key', + routeId: 'virtual-machines', + }, +]; diff --git a/src/app/core/services/index.ts b/src/app/core/services/index.ts index f9c86b5d43..ffda16ed2e 100644 --- a/src/app/core/services/index.ts +++ b/src/app/core/services/index.ts @@ -1 +1,2 @@ export * from './snack-bar.service'; +export * from '../nav-menu/nav-menu.service'; diff --git a/src/app/core/services/snack-bar.service.spec.ts b/src/app/core/services/snack-bar.service.spec.ts index a12d858596..b917d6dc43 100644 --- a/src/app/core/services/snack-bar.service.spec.ts +++ b/src/app/core/services/snack-bar.service.spec.ts @@ -1,4 +1,3 @@ -import { LiveAnnouncer } from '@angular/cdk/a11y'; import { CommonModule } from '@angular/common'; import { Component, Directive, NgModule, ViewChild, ViewContainerRef } from '@angular/core'; @@ -12,8 +11,7 @@ import { OverlayContainer } from '@angular/cdk/overlay'; @Directive({ selector: '[csViewContainer]' }) class ViewContainerDirective { - constructor(public viewContainerRef: ViewContainerRef) { - } + constructor(public viewContainerRef: ViewContainerRef) {} } @Component({ @@ -22,7 +20,8 @@ class ViewContainerDirective {
`, }) class TestComponent { - @ViewChild(ViewContainerDirective) viewContainer: ViewContainerDirective; + @ViewChild(ViewContainerDirective) + viewContainer: ViewContainerDirective; get childViewContainer() { return this.viewContainer.viewContainerRef; @@ -35,15 +34,12 @@ class TestComponent { declarations: [TestComponent, ViewContainerDirective], entryComponents: [TestComponent], }) -class NotificationTestModule { -} +class NotificationTestModule {} describe('Service: Notification service', () => { let notificationService: SnackBarService; let mdSnackBar: MatSnackBar; - let liveAnnouncer: LiveAnnouncer; let overlayContainerElement: HTMLElement; - let testViewContainerRef: ViewContainerRef; let viewContainerFixture: ComponentFixture; beforeEach(async(() => { @@ -53,32 +49,25 @@ describe('Service: Notification service', () => { SnackBarService, { provide: TranslateService, useClass: MockTranslateService }, { - provide: OverlayContainer, useFactory: () => { - overlayContainerElement = document.createElement('div'); - return { getContainerElement: () => overlayContainerElement }; - } - } - ] + provide: OverlayContainer, + useFactory: () => { + overlayContainerElement = document.createElement('div'); + return { getContainerElement: () => overlayContainerElement }; + }, + }, + ], }); })); - beforeEach(async(inject( - [SnackBarService, MatSnackBar, LiveAnnouncer], - ( - service: SnackBarService, - snackBar: MatSnackBar, - lAnnouncer: LiveAnnouncer - ) => { + beforeEach(async( + inject([SnackBarService, MatSnackBar], (service: SnackBarService, snackBar: MatSnackBar) => { notificationService = service; mdSnackBar = snackBar; - liveAnnouncer = lAnnouncer; - } - )) - ); + }), + )); afterEach(() => { overlayContainerElement.innerHTML = ''; - liveAnnouncer = undefined; mdSnackBar = undefined; notificationService = undefined; }); @@ -87,7 +76,6 @@ describe('Service: Notification service', () => { viewContainerFixture = TestBed.createComponent(TestComponent); viewContainerFixture.detectChanges(); - testViewContainerRef = viewContainerFixture.componentInstance.childViewContainer; }); it('should be defined', () => { @@ -108,5 +96,3 @@ describe('Service: Notification service', () => { expect(overlayContainerElement.querySelector('snack-bar-container')).not.toBeNull(); }); }); - - diff --git a/src/app/core/services/snack-bar.service.ts b/src/app/core/services/snack-bar.service.ts index 40977deb57..ce30a27629 100644 --- a/src/app/core/services/snack-bar.service.ts +++ b/src/app/core/services/snack-bar.service.ts @@ -6,43 +6,34 @@ import { map } from 'rxjs/operators'; import { ParametrizedTranslation } from '../../dialog/dialog-service/dialog.service'; - @Injectable() export class SnackBarService { private readonly snackBarConfig: MatSnackBarConfig; - constructor( - private snackBar: MatSnackBar, - private translateService: TranslateService - ) { + constructor(private snackBar: MatSnackBar, private translateService: TranslateService) { this.snackBarConfig = { duration: 2750 }; } public open( message: string | ParametrizedTranslation, action?: string, - config?: MatSnackBarConfig + config?: MatSnackBarConfig, ): Observable> { const message$ = this.getTranslatedString(message); const action$ = action ? this.getTranslatedString(action) : of(null); - const _config = config ? config : this.snackBarConfig; + const conf = config ? config : this.snackBarConfig; return zip(message$, action$).pipe( map(([translatedMessage, translatedAction]) => - this.snackBar.open(translatedMessage, translatedAction, _config) - ) + this.snackBar.open(translatedMessage, translatedAction, conf), + ), ); } - private getTranslatedString(message: string | ParametrizedTranslation): Observable { if (typeof message === 'string') { return this.translateService.get(message); - } else { - return this.translateService.get( - message.translationToken, - message.interpolateParams - ); } + return this.translateService.get(message.translationToken, message.interpolateParams); } } diff --git a/src/app/dialog/dialog-service/alert-dialog/alert-dialog.component.ts b/src/app/dialog/dialog-service/alert-dialog/alert-dialog.component.ts index 2f175ef3d9..02c8cb1f36 100644 --- a/src/app/dialog/dialog-service/alert-dialog/alert-dialog.component.ts +++ b/src/app/dialog/dialog-service/alert-dialog/alert-dialog.component.ts @@ -11,16 +11,15 @@ export interface AlertDialogConfiguration extends BaseDialogConfiguration { @Component({ selector: 'cs-alert-dialog', - templateUrl: 'alert-dialog.component.html' + templateUrl: 'alert-dialog.component.html', }) export class AlertDialogComponent { - public config: AlertDialogConfiguration; constructor( public dialogRef: MatDialogRef, private translateService: TranslateService, - @Inject(MAT_DIALOG_DATA) data + @Inject(MAT_DIALOG_DATA) data, ) { this.config = data.config; } @@ -28,15 +27,13 @@ export class AlertDialogComponent { public get translatedMessage(): Observable { if (typeof this.config.message === 'string') { return this.translateService.get(this.config.message); - } else { - return this.translateService.get( - this.config.message.translationToken, - this.config.message.interpolateParams - ); } + return this.translateService.get( + this.config.message.translationToken, + this.config.message.interpolateParams, + ); } - @HostListener('keydown.esc') public onEsc(): void { this.dialogRef.close(); diff --git a/src/app/dialog/dialog-service/ask-dialog/ask-dialog.component.ts b/src/app/dialog/dialog-service/ask-dialog/ask-dialog.component.ts index 6a3c34ef4e..9cd72b528c 100644 --- a/src/app/dialog/dialog-service/ask-dialog/ask-dialog.component.ts +++ b/src/app/dialog/dialog-service/ask-dialog/ask-dialog.component.ts @@ -6,7 +6,7 @@ import { Observable } from 'rxjs'; import { BaseDialogConfiguration } from '../dialog.service'; export interface AskDialogConfiguration extends BaseDialogConfiguration { - actions: Array; + actions: DialogAction[]; } export interface DialogAction { @@ -18,16 +18,15 @@ export interface DialogAction { @Component({ selector: 'cs-ask-dialog', templateUrl: 'ask-dialog.component.html', - styleUrls: ['ask-dialog.component.scss'] + styleUrls: ['ask-dialog.component.scss'], }) export class AskDialogComponent { - public config: AskDialogConfiguration; constructor( public dialogRef: MatDialogRef, private translateService: TranslateService, - @Inject(MAT_DIALOG_DATA) data + @Inject(MAT_DIALOG_DATA) data, ) { this.config = data.config; } @@ -35,12 +34,11 @@ export class AskDialogComponent { public get translatedMessage(): Observable { if (typeof this.config.message === 'string') { return this.translateService.get(this.config.message); - } else { - return this.translateService.get( - this.config.message.translationToken, - this.config.message.interpolateParams - ); } + return this.translateService.get( + this.config.message.translationToken, + this.config.message.interpolateParams, + ); } public actionClicked(action: DialogAction): void { diff --git a/src/app/dialog/dialog-service/confirm-dialog/confirm-dialog.component.ts b/src/app/dialog/dialog-service/confirm-dialog/confirm-dialog.component.ts index 8530e47633..6d9577f50c 100644 --- a/src/app/dialog/dialog-service/confirm-dialog/confirm-dialog.component.ts +++ b/src/app/dialog/dialog-service/confirm-dialog/confirm-dialog.component.ts @@ -12,16 +12,15 @@ export interface ConfirmDialogConfiguration extends BaseDialogConfiguration { @Component({ selector: 'cs-confirm-dialog', - templateUrl: 'confirm-dialog.component.html' + templateUrl: 'confirm-dialog.component.html', }) export class ConfirmDialogComponent { - public config: ConfirmDialogConfiguration; constructor( public dialogRef: MatDialogRef, private translateService: TranslateService, - @Inject(MAT_DIALOG_DATA) data + @Inject(MAT_DIALOG_DATA) data, ) { this.config = data.config; } @@ -34,11 +33,10 @@ export class ConfirmDialogComponent { public get translatedMessage(): Observable { if (typeof this.config.message === 'string') { return this.translateService.get(this.config.message); - } else { - return this.translateService.get( - this.config.message.translationToken, - this.config.message.interpolateParams - ); } + return this.translateService.get( + this.config.message.translationToken, + this.config.message.interpolateParams, + ); } } diff --git a/src/app/dialog/dialog-service/dialog.module.ts b/src/app/dialog/dialog-service/dialog.module.ts index cc2fa8475c..06b6b8ffe1 100644 --- a/src/app/dialog/dialog-service/dialog.module.ts +++ b/src/app/dialog/dialog-service/dialog.module.ts @@ -9,28 +9,10 @@ import { DialogService } from './dialog.service'; import { MaterialModule } from '../../material/material.module'; @NgModule({ - imports: [ - CommonModule, - MaterialModule, - TranslateModule, - ], - exports: [ - ConfirmDialogComponent, - AlertDialogComponent, - AskDialogComponent - ], - declarations: [ - ConfirmDialogComponent, - AlertDialogComponent, - AskDialogComponent - ], - providers: [ - DialogService, - ], - entryComponents: [ - ConfirmDialogComponent, - AlertDialogComponent, - AskDialogComponent - ], + imports: [CommonModule, MaterialModule, TranslateModule], + exports: [ConfirmDialogComponent, AlertDialogComponent, AskDialogComponent], + declarations: [ConfirmDialogComponent, AlertDialogComponent, AskDialogComponent], + providers: [DialogService], + entryComponents: [ConfirmDialogComponent, AlertDialogComponent, AskDialogComponent], }) -export class DialogModule { } +export class DialogModule {} diff --git a/src/app/dialog/dialog-service/dialog.service.ts b/src/app/dialog/dialog-service/dialog.service.ts index cf2344e3a1..5fd2f51a5d 100644 --- a/src/app/dialog/dialog-service/dialog.service.ts +++ b/src/app/dialog/dialog-service/dialog.service.ts @@ -2,9 +2,15 @@ import { Injectable } from '@angular/core'; import { MatDialog, MatDialogConfig, MatDialogRef } from '@angular/material'; import { Observable } from 'rxjs'; -import { AlertDialogComponent, AlertDialogConfiguration } from './alert-dialog/alert-dialog.component'; +import { + AlertDialogComponent, + AlertDialogConfiguration, +} from './alert-dialog/alert-dialog.component'; import { AskDialogComponent, AskDialogConfiguration } from './ask-dialog/ask-dialog.component'; -import { ConfirmDialogComponent, ConfirmDialogConfiguration } from './confirm-dialog/confirm-dialog.component'; +import { + ConfirmDialogComponent, + ConfirmDialogConfiguration, +} from './confirm-dialog/confirm-dialog.component'; const defaultConfirmDialogConfirmText = 'COMMON.YES'; const defaultConfirmDialogDeclineText = 'COMMON.NO'; @@ -14,7 +20,7 @@ const defaultWidth = '400px'; export interface ParametrizedTranslation { translationToken: string; - interpolateParams: { [key: string]: string; }; + interpolateParams: { [key: string]: string }; } export interface BaseDialogConfiguration { @@ -24,11 +30,9 @@ export interface BaseDialogConfiguration { width?: string; } - @Injectable() export class DialogService { - - constructor(private dialog: MatDialog) { } + constructor(private dialog: MatDialog) {} public confirm(config: ConfirmDialogConfiguration): Observable { let dialogRef: MatDialogRef; @@ -66,7 +70,7 @@ export class DialogService { config.actions = config.actions.map(action => ({ handler: action.handler || (() => {}), text: action.text, - isClosingAction: action.isClosingAction + isClosingAction: action.isClosingAction, })); dialogRef = this.dialog.open(AskDialogComponent, this.getDialogConfiguration(config)); @@ -74,12 +78,12 @@ export class DialogService { } private getDialogConfiguration(config: BaseDialogConfiguration) { - const configuration = { + const configuration = { data: { config }, - disableClose: config.disableClose - }; - return config.width ? - Object.assign(configuration, { width: config.width }) : - Object.assign(configuration, { width: defaultWidth }); + disableClose: config.disableClose, + } as MatDialogConfig; + return config.width + ? { ...configuration, width: config.width } + : { ...configuration, width: defaultWidth }; } } diff --git a/src/app/events/components/event-list.component.html b/src/app/events/components/event-list.component.html index f18d7c1a58..d6abc96857 100644 --- a/src/app/events/components/event-list.component.html +++ b/src/app/events/components/event-list.component.html @@ -6,16 +6,16 @@ [okLabel]="'COMMON.OK' | translate" [locale]="locale" [firstDayOfWeek]="firstDayOfWeek" - [DateTimeFormat]="dateTimeFormatterService.dateTimeFormat" + [dateTimeFormat]="dateTimeFormatterService.dateTimeFormat" [cancelLabel]="'COMMON.CANCEL' | translate" [ngModel]="date" - (change)="onDateChange.emit($event)" + (changed)="dateChange.emit($event)" > @@ -30,7 +30,7 @@ @@ -47,7 +47,7 @@ multiple="true" [placeholder]="'VM_PAGE.FILTERS.SELECT_ACCOUNTS' | translate" [(ngModel)]="selectedAccountIds" - (selectionChange)="onAccountChange.emit($event.value)" + (selectionChange)="accountChanged.emit($event.value)" >
diff --git a/src/app/events/components/event-list.component.ts b/src/app/events/components/event-list.component.ts index 1ef97c7c51..d2c80aec20 100644 --- a/src/app/events/components/event-list.component.ts +++ b/src/app/events/components/event-list.component.ts @@ -5,7 +5,7 @@ import { Input, OnChanges, Output, - SimpleChanges + SimpleChanges, } from '@angular/core'; import { MatTableDataSource } from '@angular/material'; import { TranslateService } from '@ngx-translate/core'; @@ -19,25 +19,41 @@ import { Language } from '../../shared/types'; selector: 'cs-event-list', templateUrl: 'event-list.component.html', changeDetection: ChangeDetectionStrategy.OnPush, - styleUrls: ['event-list.component.scss'] + styleUrls: ['event-list.component.scss'], }) export class EventListComponent implements OnChanges { - @Input() public events: Event[] = []; - @Input() public selectedTypes: string[] = []; - @Input() public selectedLevels: string[] = []; - @Input() public eventTypes: string[] = []; - @Input() public firstDayOfWeek: number; - @Input() public isLoading = false; - @Input() public date: Date; - @Input() public query: string; - @Input() public accounts: Account[] = []; - @Input() public selectedAccountIds: string[] = []; - @Input() public isAdmin: boolean; - @Output() public onDateChange = new EventEmitter(); - @Output() public onQueryChange = new EventEmitter(); - @Output() public onEventTypesChange = new EventEmitter>(); - @Output() public onSelectedLevelsChange = new EventEmitter>(); - @Output() public onAccountChange = new EventEmitter>(); + @Input() + public events: Event[] = []; + @Input() + public selectedTypes: string[] = []; + @Input() + public selectedLevels: string[] = []; + @Input() + public eventTypes: string[] = []; + @Input() + public firstDayOfWeek: number; + @Input() + public isLoading = false; + @Input() + public date: Date; + @Input() + public query: string; + @Input() + public accounts: Account[] = []; + @Input() + public selectedAccountIds: string[] = []; + @Input() + public isAdmin: boolean; + @Output() + public dateChange = new EventEmitter(); + @Output() + public queryChanged = new EventEmitter(); + @Output() + public eventTypesChanged = new EventEmitter(); + @Output() + public selectedLevelsChanged = new EventEmitter(); + @Output() + public accountChanged = new EventEmitter(); public dataSource: MatTableDataSource; public tableColumns = ['description', 'level', 'type', 'time']; @@ -45,7 +61,7 @@ export class EventListComponent implements OnChanges { constructor( public dateTimeFormatterService: DateTimeFormatterService, - public translate: TranslateService + public translate: TranslateService, ) { this.dataSource = new MatTableDataSource([]); } diff --git a/src/app/events/containers/event-list.container.ts b/src/app/events/containers/event-list.container.ts index c5a543e7bb..7d10665c9f 100644 --- a/src/app/events/containers/event-list.container.ts +++ b/src/app/events/containers/event-list.container.ts @@ -32,15 +32,14 @@ const FILTER_KEY = 'eventListFilters'; [accounts]="accounts$ | async" [isAdmin]="isAdmin()" [selectedAccountIds]="selectedAccountIds$ | async" - (onAccountChange)="onAccountChange($event)" - (onDateChange)="onDateChange($event)" - (onQueryChange)="onQueryChange($event)" - (onEventTypesChange)="onEventTypesChange($event)" - (onSelectedLevelsChange)="onSelectedLevelsChange($event)" - >` + (accountChanged)="onAccountChange($event)" + (dateChange)="onDateChange($event)" + (queryChanged)="onQueryChange($event)" + (eventTypesChanged)="onEventTypesChange($event)" + (selectedLevelsChanged)="onSelectedLevelsChange($event)" + >`, }) export class EventListContainerComponent extends WithUnsubscribe() implements OnInit { - readonly firstDayOfWeek$ = this.store.pipe(select(UserTagsSelectors.getFirstDayOfWeek)); readonly events$ = this.store.pipe(select(fromEvents.selectFilteredEvents)); readonly accounts$ = this.store.pipe(select(fromAccounts.selectAll)); @@ -56,23 +55,24 @@ export class EventListContainerComponent extends WithUnsubscribe() implements On map(([all, selected]) => { const set = new Set(all.concat(selected)); return [...Array.from(set)]; - })); + }), + ); readonly date$ = this.store.pipe(select(fromEvents.filterDate)); public levels = ['INFO', 'WARN', 'ERROR']; private filterService = new FilterService( { - 'date': { type: 'string' }, - 'levels': { type: 'array', options: this.levels, defaultOption: [] }, - 'types': { type: 'array', defaultOption: [] }, - 'accounts': { type: 'array', defaultOption: [] }, - 'query': { type: 'string' } + date: { type: 'string' }, + levels: { type: 'array', options: this.levels, defaultOption: [] }, + types: { type: 'array', defaultOption: [] }, + accounts: { type: 'array', defaultOption: [] }, + query: { type: 'string' }, }, this.router, this.sessionStorage, FILTER_KEY, - this.activatedRoute + this.activatedRoute, ); constructor( @@ -95,15 +95,15 @@ export class EventListContainerComponent extends WithUnsubscribe() implements On this.store.dispatch(new eventAction.EventFilterUpdate({ query })); } - public onEventTypesChange(selectedTypes: Array) { + public onEventTypesChange(selectedTypes: string[]) { this.store.dispatch(new eventAction.EventFilterUpdate({ selectedTypes })); } - public onSelectedLevelsChange(selectedLevels: Array) { + public onSelectedLevelsChange(selectedLevels: string[]) { this.store.dispatch(new eventAction.EventFilterUpdate({ selectedLevels })); } - public onAccountChange(selectedAccountIds: Array) { + public onAccountChange(selectedAccountIds: string[]) { this.store.dispatch(new eventAction.EventFilterUpdate({ selectedAccountIds })); } @@ -115,17 +115,15 @@ export class EventListContainerComponent extends WithUnsubscribe() implements On this.store.dispatch(new accountAction.LoadAccountsRequest()); this.initFilters(); - this.filters$.pipe( - takeUntil(this.unsubscribe$)) - .subscribe(filters => { - this.filterService.update({ - 'date': moment(filters.date).format('YYYY-MM-DD'), - 'levels': filters.selectedLevels, - 'types': filters.selectedTypes, - 'accounts': filters.selectedAccountIds, - 'query': filters.query - }); + this.filters$.pipe(takeUntil(this.unsubscribe$)).subscribe(filters => { + this.filterService.update({ + date: moment(filters.date).format('YYYY-MM-DD'), + levels: filters.selectedLevels, + types: filters.selectedTypes, + accounts: filters.selectedAccountIds, + query: filters.query, }); + }); } private initFilters(): void { @@ -137,13 +135,14 @@ export class EventListContainerComponent extends WithUnsubscribe() implements On const query = params['query']; const selectedAccountIds = params['accounts']; - this.store.dispatch(new eventAction.EventFilterUpdate({ - query, - date, - selectedTypes, - selectedLevels, - selectedAccountIds - })); + this.store.dispatch( + new eventAction.EventFilterUpdate({ + query, + date, + selectedTypes, + selectedLevels, + selectedAccountIds, + }), + ); } - } diff --git a/src/app/events/event.model.ts b/src/app/events/event.model.ts index 7118ad97a8..45669a9923 100644 --- a/src/app/events/event.model.ts +++ b/src/app/events/event.model.ts @@ -1,6 +1,6 @@ -import { BaseModelInterface } from '../shared/models/base.model'; +import { BaseModel } from '../shared/models/base.model'; -export interface Event extends BaseModelInterface { +export interface Event extends BaseModel { id: string; type: string; time: string; diff --git a/src/app/events/event.service.ts b/src/app/events/event.service.ts index 1a3cc0ab76..82a2b247a0 100644 --- a/src/app/events/event.service.ts +++ b/src/app/events/event.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { BackendResource } from '../shared/decorators'; -import { BaseBackendService } from '../shared/services/base-backend.service'; +import { BaseBackendService, FormattedResponse } from '../shared/services/base-backend.service'; import { Event } from './event.model'; import { HttpClient } from '@angular/common/http'; @@ -9,20 +9,26 @@ import { DateTimeFormatterService } from '../shared/services/date-time-formatter @Injectable() @BackendResource({ - entity: 'Event' + entity: 'Event', }) export class EventService extends BaseBackendService { constructor( protected http: HttpClient, - private dateTimeFormatterService: DateTimeFormatterService + private dateTimeFormatterService: DateTimeFormatterService, ) { super(http); } - protected prepareModel(res, entityModel?): Event { - const event = super.prepareModel(res, this.entityModel); + protected formatGetListResponse(response: any): FormattedResponse { + const result = super.formatGetListResponse(response); + return { + list: result.list.map(m => this.prepareEventModel(m)), + meta: result.meta, + }; + } - event.created = moment(res.created).toDate(); + private prepareEventModel(event): Event { + event.created = moment(event.created).toDate(); event.time = this.dateTimeFormatterService.stringifyToTime(event.created); return event; } diff --git a/src/app/events/events.module.ts b/src/app/events/events.module.ts index 9178429ab0..1d4144c2a3 100644 --- a/src/app/events/events.module.ts +++ b/src/app/events/events.module.ts @@ -19,15 +19,8 @@ import { EventListComponent } from './components/event-list.component'; MaterialModule, StoreModule.forFeature('events', reducers), EffectsModule.forFeature([EventsEffects]), - - ], - declarations: [ - EventListContainerComponent, - EventListComponent, ], - providers: [ - EventService - ] + declarations: [EventListContainerComponent, EventListComponent], + providers: [EventService], }) -export class EventsModule { -} +export class EventsModule {} diff --git a/src/app/events/redux/events.actions.ts b/src/app/events/redux/events.actions.ts index 4424acea18..9b1e46f7d3 100644 --- a/src/app/events/redux/events.actions.ts +++ b/src/app/events/redux/events.actions.ts @@ -8,26 +8,19 @@ export const EVENT_FILTER_UPDATE = '[Events] EVENT_FILTER_UPDATE'; export class LoadEventsRequest implements Action { type = LOAD_EVENTS_REQUEST; - constructor(public payload: any) { - } - + constructor(public payload: any) {} } export class LoadEventsResponse implements Action { type = LOAD_EVENTS_RESPONSE; - constructor(public payload: Array) { - } - + constructor(public payload: Event[]) {} } export class EventFilterUpdate implements Action { type = EVENT_FILTER_UPDATE; - constructor(public payload: { [key: string]: any }) { - } - + constructor(public payload: { [key: string]: any }) {} } - export type Actions = LoadEventsResponse | LoadEventsRequest | EventFilterUpdate; diff --git a/src/app/events/redux/events.effects.ts b/src/app/events/redux/events.effects.ts index 304c0ae1a6..703f699a9c 100644 --- a/src/app/events/redux/events.effects.ts +++ b/src/app/events/redux/events.effects.ts @@ -16,26 +16,27 @@ export class EventsEffects { ofType(event.EVENT_FILTER_UPDATE), filter((action: event.EventFilterUpdate) => action.payload.date), map((action: event.EventFilterUpdate) => action.payload.date), - map(date => new event.LoadEventsRequest({ - startDate: formatIso(date), - endDate: formatIso(date) - }))); + map( + date => + new event.LoadEventsRequest({ + startDate: formatIso(date), + endDate: formatIso(date), + }), + ), + ); @Effect() loadEvents$: Observable = this.actions$.pipe( ofType(event.LOAD_EVENTS_REQUEST), switchMap((action: event.LoadEventsRequest) => { - return this.eventService - .getListAll(action.payload).pipe( - map((events: Event[]) => { - return new event.LoadEventsResponse(events); - }), - catchError(() => of(new event.LoadEventsResponse([])))); - })); + return this.eventService.getListAll(action.payload).pipe( + map((events: Event[]) => { + return new event.LoadEventsResponse(events); + }), + catchError(() => of(new event.LoadEventsResponse([]))), + ); + }), + ); - constructor( - private actions$: Actions, - private eventService: EventService - ) { - } + constructor(private actions$: Actions, private eventService: EventService) {} } diff --git a/src/app/events/redux/events.reducers.ts b/src/app/events/redux/events.reducers.ts index 7a157a3934..35924f98a0 100644 --- a/src/app/events/redux/events.reducers.ts +++ b/src/app/events/redux/events.reducers.ts @@ -13,15 +13,15 @@ import moment = require('moment'); * any additional interface properties. */ export interface State extends EntityState { - loading: boolean, - eventTypes: string[], + loading: boolean; + eventTypes: string[]; filters: { - date: Date, - selectedTypes: string[], - selectedLevels: string[], - selectedAccountIds: string[], - query: string - } + date: Date; + selectedTypes: string[]; + selectedLevels: string[]; + selectedAccountIds: string[]; + query: string; + }; } export interface EventsState { @@ -42,7 +42,7 @@ export const reducers = { */ export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: Event) => item.id, - sortComparer: false + sortComparer: false, }); /** getInitialState returns the default initial state @@ -57,19 +57,16 @@ export const initialState: State = adapter.getInitialState({ selectedTypes: [], selectedLevels: [], selectedAccountIds: [], - query: '' - } + query: '', + }, }); -export function reducer( - state = initialState, - action: eventActions.Actions -): State { +export function reducer(state = initialState, action: eventActions.Actions): State { switch (action.type) { case eventActions.LOAD_EVENTS_REQUEST: { return { ...state, - loading: true + loading: true, }; } case eventActions.EVENT_FILTER_UPDATE: { @@ -77,16 +74,17 @@ export function reducer( ...state, filters: { ...state.filters, - ...action.payload - } + ...action.payload, + }, }; } case eventActions.LOAD_EVENTS_RESPONSE: { - const events = action.payload; - const types = Object.keys(events.reduce((memo, event) => { - return { ...memo, [event.type]: event.type }; - }, {})); + const types = Object.keys( + events.reduce((memo, event) => { + return { ...memo, [event.type]: event.type }; + }, {}), + ); return { /** @@ -98,73 +96,39 @@ export function reducer( */ ...adapter.addAll(events, state), eventTypes: types, - loading: false + loading: false, }; } - default: { return state; } } } - export const getEventsState = createFeatureSelector('events'); -export const getEventsEntitiesState = createSelector( - getEventsState, - state => state.list -); - -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getEventsEntitiesState); - -export const isLoading = createSelector( - getEventsEntitiesState, - state => state.loading -); +export const getEventsEntitiesState = createSelector(getEventsState, state => state.list); -export const eventTypes = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getEventsEntitiesState, - state => state.eventTypes ); +export const isLoading = createSelector(getEventsEntitiesState, state => state.loading); -export const filters = createSelector( - getEventsEntitiesState, - state => state.filters -); +export const eventTypes = createSelector(getEventsEntitiesState, state => state.eventTypes); -export const filterDate = createSelector( - filters, - state => state.date -); +export const filters = createSelector(getEventsEntitiesState, state => state.filters); +export const filterDate = createSelector(filters, state => state.date); -export const filterQuery = createSelector( - filters, - state => state.query -); +export const filterQuery = createSelector(filters, state => state.query); -export const filterSelectedTypes = createSelector( - filters, - state => state.selectedTypes -); +export const filterSelectedTypes = createSelector(filters, state => state.selectedTypes); -export const filterSelectedLevels = createSelector( - filters, - state => state.selectedLevels -); +export const filterSelectedLevels = createSelector(filters, state => state.selectedLevels); -export const filterSelectedAccountIds = createSelector( - filters, - state => state.selectedAccountIds -); +export const filterSelectedAccountIds = createSelector(filters, state => state.selectedAccountIds); export const selectFilteredEvents = createSelector( selectAll, @@ -178,8 +142,9 @@ export const selectFilteredEvents = createSelector( const typeMap = selectedTypes.reduce((m, i) => ({ ...m, [i]: i }), {}); const levelsMap = selectedLevels.reduce((m, i) => ({ ...m, [i]: i }), {}); - const queryFilter = event => !query || event.description.toLowerCase() - .includes(queryLower) || + const queryFilter = event => + !query || + event.description.toLowerCase().includes(queryLower) || event.level.toLowerCase().includes(queryLower) || event.type.toLowerCase().includes(queryLower) || event.time.toLowerCase().includes(queryLower); @@ -187,28 +152,27 @@ export const selectFilteredEvents = createSelector( const selectedTypesFilter = event => !selectedTypes.length || !!typeMap[event.type]; const selectedLevelsFilter = event => { - return !selectedLevels.length || - !!levelsMap[event.level]; + return !selectedLevels.length || !!levelsMap[event.level]; }; - const accountsMap = selectedAccountIds - .reduce((memo, id) => { - const account = accountEntities[id]; - if (account) { - return { ...memo, [`${account.name}_${account.domain}`]: account }; - } - return memo; - }, {}); + const accountsMap = selectedAccountIds.reduce((memo, id) => { + const account = accountEntities[id]; + if (account) { + return { ...memo, [`${account.name}_${account.domain}`]: account }; + } + return memo; + }, {}); - const selectedAccountIdsFilter = event => !selectedAccountIds.length || - accountsMap[`${event.account}_${event.domain}`]; + const selectedAccountIdsFilter = event => + !selectedAccountIds.length || accountsMap[`${event.account}_${event.domain}`]; return events.filter(event => { - return queryFilter(event) - && selectedTypesFilter(event) - && selectedLevelsFilter(event) - && selectedAccountIdsFilter(event); + return ( + queryFilter(event) && + selectedTypesFilter(event) && + selectedLevelsFilter(event) && + selectedAccountIdsFilter(event) + ); }); - } + }, ); - diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index 88003d7fe0..6004fe382a 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -1,7 +1,12 @@ - - + + - + + +
+ +
+
diff --git a/src/app/home/home.component.scss b/src/app/home/home.component.scss index e43e5bf102..c2e7ac665b 100644 --- a/src/app/home/home.component.scss +++ b/src/app/home/home.component.scss @@ -1,17 +1,9 @@ -mat-sidenav-container { - mat-sidenav { - display: flex; - flex-direction: column; - flex-wrap: nowrap; - width: 216px; - height: 100%; - max-height: 100%; - will-change: transform; - color: #424242; - overflow: hidden; - } +mat-sidenav-content { + display: flex; - cs-sidenav { - overflow: auto; + .primary-content { + width: 100%; + height: 100%; + overflow-x: auto; } } diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index f8e1ed5932..08e91e12fd 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -1,46 +1,63 @@ import { Component, OnInit } from '@angular/core'; import { select, Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; -import { configSelectors, layoutSelectors, State, UserTagsActions } from '../root-store'; +import { State, UserTagsActions, layoutStore } from '../root-store'; import { AuthService } from '../shared/services/auth.service'; import { WithUnsubscribe } from '../utils/mixins/with-unsubscribe'; -import { getName } from '../shared/models'; +import { Route, Subroute } from '../core/nav-menu/models'; +import { NavMenuService } from '../core/services'; import * as authActions from '../reducers/auth/redux/auth.actions'; @Component({ selector: 'cs-home', templateUrl: './home.component.html', - styleUrls: ['./home.component.scss'] + styleUrls: ['./home.component.scss'], }) export class HomeComponent extends WithUnsubscribe() implements OnInit { public disableSecurityGroups = false; - public isSidenavVisible$ = this.store.pipe(select(layoutSelectors.isSidenavVisible)); - public allowReorderingSidenav$ = this.store.pipe(select(configSelectors.get('allowReorderingSidenav'))); + public routes$: Observable = this.navRoutesService.getRoutes(); + public currentRoute$: Observable = this.navRoutesService.getCurrentRoute(); + public subroutes$: Observable = this.navRoutesService.getSubroutes(); + public showAppNav$: Observable = this.store.pipe( + select(layoutStore.selectors.getShowAppNav), + ); + public username: string; constructor( private auth: AuthService, - private store: Store + private store: Store, + private navRoutesService: NavMenuService, ) { super(); + this.username = this.auth.user ? this.auth.user.username : ''; } public ngOnInit(): void { this.store.dispatch(new UserTagsActions.LoadUserTags()); - this.auth.loggedIn.pipe( - takeUntil(this.unsubscribe$), - filter(isLoggedIn => isLoggedIn)) + this.auth.loggedIn + .pipe( + takeUntil(this.unsubscribe$), + filter(isLoggedIn => isLoggedIn), + ) .subscribe(() => { - this.store.dispatch(new authActions.LoadUserAccountRequest({ - name: this.auth.user.account, - domainid: this.auth.user.domainid - })); + this.store.dispatch( + new authActions.LoadUserAccountRequest({ + name: this.auth.user.account, + domainid: this.auth.user.domainid, + }), + ); this.disableSecurityGroups = this.auth.isSecurityGroupEnabled(); }); } - public get title(): string { - return this.auth.user ? getName(this.auth.user) : ''; + public openAppNav() { + this.store.dispatch(new layoutStore.actions.OpenAppNav()); + } + + public closeAppNav() { + this.store.dispatch(new layoutStore.actions.CloseAppNav()); } } diff --git a/src/app/material/material.module.ts b/src/app/material/material.module.ts index ba45f081a5..22a93a256c 100644 --- a/src/app/material/material.module.ts +++ b/src/app/material/material.module.ts @@ -48,11 +48,9 @@ const MATERIAL_MODULES = [ MatTooltipModule, ]; - @NgModule({ imports: MATERIAL_MODULES, - exports: MATERIAL_MODULES - + exports: MATERIAL_MODULES, }) export class MaterialModule { constructor(matIconRegistry: MatIconRegistry) { diff --git a/src/app/pulse/charts/aggregation-selector.component.ts b/src/app/pulse/charts/aggregation-selector.component.ts index 3b00b9d32f..9443f1c24d 100644 --- a/src/app/pulse/charts/aggregation-selector.component.ts +++ b/src/app/pulse/charts/aggregation-selector.component.ts @@ -5,7 +5,7 @@ import { Input, Output, ViewChild, - ViewEncapsulation + ViewEncapsulation, } from '@angular/core'; import { AbstractControl } from '@angular/forms'; import { MatOptionSelectionChange, MatSelectChange } from '@angular/material'; @@ -15,7 +15,7 @@ import * as debounce from 'lodash/debounce'; selector: 'cs-aggregation-selector', templateUrl: 'aggregation-selector.component.html', styles: [ - ` + ` .aggregation-select { margin: 30px 10px 20px; } @@ -27,25 +27,33 @@ import * as debounce from 'lodash/debounce'; .shift-select { width: 100px; } - ` + `, ], encapsulation: ViewEncapsulation.None, - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class AggregationSelectorComponent { - @Input() permittedIntervals: any; - @Output() scaleChange = new EventEmitter(); - @Output() aggregationsChange = new EventEmitter(); - @Output() refreshHandle = new EventEmitter(); - @Output() shiftChange = new EventEmitter(); - @Output() shiftAmountChange = new EventEmitter(); - - @ViewChild('aggregationSelect') aggregationSelectControl: AbstractControl; - - selectedScale: { aggregations: Array }; + @Input() + permittedIntervals: any; + @Output() + scaleChange = new EventEmitter(); + @Output() + aggregationsChange = new EventEmitter(); + @Output() + refreshHandle = new EventEmitter(); + @Output() + shiftChange = new EventEmitter(); + @Output() + shiftAmountChange = new EventEmitter(); + + @ViewChild('aggregationSelect') + aggregationSelectControl: AbstractControl; + + selectedScale: { aggregations: any[] }; selectedShift: string; - @Input() public shiftAmount: number; - selectedAggregations: Array; + @Input() + public shiftAmount: number; + selectedAggregations: string[]; constructor() { this.emitShiftChange = debounce(this.emitShiftChange, 300); diff --git a/src/app/pulse/charts/chart-area.component.ts b/src/app/pulse/charts/chart-area.component.ts index 7f65e51f15..af5963441e 100644 --- a/src/app/pulse/charts/chart-area.component.ts +++ b/src/app/pulse/charts/chart-area.component.ts @@ -4,23 +4,27 @@ import { PulseChart } from './pulse-chart'; @Component({ selector: 'cs-chart-area-component', templateUrl: './chart-area.component.html', - styleUrls: ['./chart-area.component.scss'] + styleUrls: ['./chart-area.component.scss'], }) export class ChartAreaComponent { - @Input() public charts: Array; - @Input() public hasNext: boolean; - @Input() public fetching; - @Input() public error; - @Output() previous = new EventEmitter(); - @Output() next = new EventEmitter(); + @Input() + public charts: PulseChart[]; + @Input() + public hasNext: boolean; + @Input() + public fetching; + @Input() + public error; + @Output() + previous = new EventEmitter(); + @Output() + next = new EventEmitter(); public hasDatasets(chart: PulseChart) { return !!chart && !!chart.datasets && !!chart.datasets.length; } public hasData(chart: PulseChart) { - return this.hasDatasets(chart) && - !!chart.datasets[0].data && - !!chart.datasets[0].data.length; + return this.hasDatasets(chart) && !!chart.datasets[0].data && !!chart.datasets[0].data.length; } } diff --git a/src/app/pulse/charts/chart.directive.ts b/src/app/pulse/charts/chart.directive.ts index da79fa48c6..5a258000ec 100644 --- a/src/app/pulse/charts/chart.directive.ts +++ b/src/app/pulse/charts/chart.directive.ts @@ -1,13 +1,20 @@ import { - Directive, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, - Output, SimpleChanges + Directive, + ElementRef, + EventEmitter, + Input, + OnChanges, + OnDestroy, + OnInit, + Output, + SimpleChanges, } from '@angular/core'; import { Chart } from 'chart.js'; @Directive({ selector: 'canvas[csBaseChart]', exportAs: 'cs-base-chart' }) export class BaseChartDirective implements OnDestroy, OnChanges, OnInit { - public static defaultColors: Array = [ + public static defaultColors: number[][] = [ [255, 99, 132], [54, 162, 235], [255, 206, 86], @@ -19,23 +26,31 @@ export class BaseChartDirective implements OnDestroy, OnChanges, OnInit { [70, 191, 189], [253, 180, 92], [148, 159, 177], - [77, 83, 96] + [77, 83, 96], ]; - @Input() public data: number[] | any[]; - @Input() public datasets: any[]; - @Input() public labels: Array = []; - @Input() public options: any = {}; - @Input() public chartType: string; - @Input() public colors: Array; - @Input() public legend: boolean; - - @Output() public chartClick: EventEmitter = new EventEmitter(); - @Output() public chartHover: EventEmitter = new EventEmitter(); + @Input() + public data: number[] | any[]; + @Input() + public datasets: any[]; + @Input() + public labels: any[] = []; + @Input() + public options: any = {}; + @Input() + public chartType: string; + @Input() + public colors: any[]; + @Input() + public legend: boolean; + + @Output() + public chartClick: EventEmitter = new EventEmitter(); + @Output() + public chartHover: EventEmitter = new EventEmitter(); public ctx: any; public chart: any; - private cvs: any; private initFlag = false; private element: ElementRef; @@ -44,9 +59,8 @@ export class BaseChartDirective implements OnDestroy, OnChanges, OnInit { this.element = element; } - public ngOnInit(): any { + public ngOnInit() { this.ctx = this.element.nativeElement.getContext('2d'); - this.cvs = this.element.nativeElement; this.initFlag = true; if (this.data || this.datasets) { this.refresh(); @@ -71,24 +85,24 @@ export class BaseChartDirective implements OnDestroy, OnChanges, OnInit { } } - public ngOnDestroy(): any { + public ngOnDestroy() { if (this.chart) { this.chart.destroy(); this.chart = void 0; } } - public getChartBuilder(ctx: any/*, data:Array, options:any*/): any { + public getChartBuilder(ctx: any): any { const datasets: any = this.getDatasets(); - const options: any = Object.assign({}, this.options); - if (this.legend === false) { + const options: any = { ...this.options }; + if (!this.legend) { options.legend = { display: false }; } // hock for onHover and onClick events options.hover = options.hover || {}; if (!options.hover.onHover) { - options.hover.onHover = (active: Array) => { + options.hover.onHover = (active: any[]) => { if (active && !active.length) { return; } @@ -97,18 +111,18 @@ export class BaseChartDirective implements OnDestroy, OnChanges, OnInit { } if (!options.onClick) { - options.onClick = (event: any, active: Array) => { + options.onClick = (event: any, active: any[]) => { this.chartClick.emit({ event, active }); }; } const opts = { + options, type: this.chartType, data: { + datasets, labels: this.labels, - datasets: datasets }, - options: options }; return new Chart(ctx, opts); @@ -135,9 +149,9 @@ export class BaseChartDirective implements OnDestroy, OnChanges, OnInit { private getDatasets(): any { let datasets: any = void 0; // in case if datasets is not provided, but data is present - if (!this.datasets || !this.datasets.length && (this.data && this.data.length)) { + if (!this.datasets || (!this.datasets.length && (this.data && this.data.length))) { if (Array.isArray(this.data[0])) { - datasets = (this.data as Array).map((data: number[], index: number) => { + datasets = (this.data as number[][]).map((data: number[], index: number) => { return { data, label: this.labels[index] || `Label ${index}` }; }); } else { @@ -145,18 +159,16 @@ export class BaseChartDirective implements OnDestroy, OnChanges, OnInit { } } - if (this.datasets && this.datasets.length || - (datasets && datasets.length)) { - datasets = (this.datasets || datasets) - .map((elm: number, index: number) => { - const newElm: any = Object.assign({}, elm); - if (this.colors && this.colors.length) { - Object.assign(newElm, this.colors[index]); - } else { - Object.assign(newElm, getColors(this.chartType, index, newElm.data.length)); - } - return newElm; - }); + if ((this.datasets && this.datasets.length) || (datasets && datasets.length)) { + datasets = (this.datasets || datasets).map((elm: any, index: number) => { + const newElm: any = { ...elm }; + if (this.colors && this.colors.length) { + Object.assign(newElm, this.colors[index]); + } else { + Object.assign(newElm, getColors(this.chartType, index, newElm.data.length)); + } + return newElm; + }); } if (!datasets) { @@ -174,7 +186,7 @@ export class BaseChartDirective implements OnDestroy, OnChanges, OnInit { // todo: remove this line, it is producing flickering this.ngOnDestroy(); - this.chart = this.getChartBuilder(this.ctx/*, data, this.options*/); + this.chart = this.getChartBuilder(this.ctx /*, data, this.options*/); } } @@ -212,51 +224,52 @@ export interface Colors extends Color { label?: string; } -function rgba(colour: Array, alpha: number): string { - return 'rgba(' + colour.concat(alpha).join(',') + ')'; +function rgba(colour: number[], alpha: number): string { + const rgbaValues = colour.concat(alpha).join(','); + return `rgba(${rgbaValues})`; } function getRandomInt(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1)) + min; } -function formatLineColor(colors: Array): Color { +function formatLineColor(colors: number[]): Color { return { backgroundColor: rgba(colors, 0.4), borderColor: rgba(colors, 1), pointBackgroundColor: rgba(colors, 1), pointBorderColor: '#fff', pointHoverBackgroundColor: '#fff', - pointHoverBorderColor: rgba(colors, 0.8) + pointHoverBorderColor: rgba(colors, 0.8), }; } -function formatBarColor(colors: Array): Color { +function formatBarColor(colors: number[]): Color { return { backgroundColor: rgba(colors, 0.6), borderColor: rgba(colors, 1), hoverBackgroundColor: rgba(colors, 0.8), - hoverBorderColor: rgba(colors, 1) + hoverBorderColor: rgba(colors, 1), }; } -function formatPieColors(colors: Array): Colors { +function formatPieColors(colors: number[][]): Colors { return { backgroundColor: colors.map((color: number[]) => rgba(color, 0.6)), borderColor: colors.map(() => '#fff'), pointBackgroundColor: colors.map((color: number[]) => rgba(color, 1)), pointBorderColor: colors.map(() => '#fff'), pointHoverBackgroundColor: colors.map((color: number[]) => rgba(color, 1)), - pointHoverBorderColor: colors.map((color: number[]) => rgba(color, 1)) + pointHoverBorderColor: colors.map((color: number[]) => rgba(color, 1)), }; } -function formatPolarAreaColors(colors: Array): Color { +function formatPolarAreaColors(colors: number[][]): Color { return { backgroundColor: colors.map((color: number[]) => rgba(color, 0.6)), borderColor: colors.map((color: number[]) => rgba(color, 1)), hoverBackgroundColor: colors.map((color: number[]) => rgba(color, 0.8)), - hoverBorderColor: colors.map((color: number[]) => rgba(color, 1)) + hoverBorderColor: colors.map((color: number[]) => rgba(color, 1)), }; } @@ -278,9 +291,9 @@ function generateColor(index: number): number[] { * @param count * @returns {Colors} */ -function generateColors(count: number): Array { - const colorsArr: Array = new Array(count); - for (let i = 0; i < count; i++) { +function generateColors(count: number): number[][] { + const colorsArr: number[][] = []; + for (let i = 0; i < count; i += 1) { colorsArr[i] = BaseChartDirective.defaultColors[i] || getRandomColor(); } return colorsArr; diff --git a/src/app/pulse/charts/pulse-chart.ts b/src/app/pulse/charts/pulse-chart.ts index d4a5428503..5790f3052a 100644 --- a/src/app/pulse/charts/pulse-chart.ts +++ b/src/app/pulse/charts/pulse-chart.ts @@ -1,41 +1,34 @@ -import { - ChangeDetectorRef, - EventEmitter, - Injectable, - Input, - Output -} from '@angular/core'; +import { ChangeDetectorRef, EventEmitter, Injectable, Input, Output } from '@angular/core'; import { PulseService } from '../pulse.service'; -import Chart = require('chart.js'); +import chartJs = require('chart.js'); -(Chart.defaults.global.elements.line as any).cubicInterpolationMode = 'monotone'; -Chart.defaults.global.elements.point.radius = 0; -Chart.defaults.global.elements.point.hitRadius = 5; +(chartJs.defaults.global.elements.line as any).cubicInterpolationMode = 'monotone'; +chartJs.defaults.global.elements.point.radius = 0; +chartJs.defaults.global.elements.point.hitRadius = 5; export interface PulseChart { id: string; width?: number; height?: number; - datasets?: Array; + datasets?: any[]; chartType?: 'line' | 'bar'; options?: any; - labels?: Array; + labels?: any[]; } - export const defaultChartOptions = { maintainAspectRatio: false, legend: { labels: { - boxWidth: 20 - } + boxWidth: 20, + }, }, layout: { padding: { left: 80, - right: 40 - } + right: 40, + }, }, tooltips: { mode: 'x', @@ -43,7 +36,7 @@ export const defaultChartOptions = { }, hover: { mode: 'nearest', - intersect: false + intersect: false, }, scales: { xAxes: [ @@ -55,10 +48,10 @@ export const defaultChartOptions = { displayFormats: { second: 'LTS', minute: 'LT', - hour: 'LT' - } - } - } + hour: 'LT', + }, + }, + }, ], yAxes: [ { @@ -71,11 +64,11 @@ export const defaultChartOptions = { if (val % 1 === 0) { return val; } - } - } - } - ] - } + }, + }, + }, + ], + }, }; export const defaultChartConfig = { @@ -84,31 +77,43 @@ export const defaultChartConfig = { datasets: [], chartType: 'line', options: defaultChartOptions, - labels: null + labels: null, }; -export function getChart(config: Array) { +export function getChart(config: any[]) { return config.map(_ => { - const options = Object.assign({}, defaultChartOptions, _.options); - return Object.assign({}, defaultChartConfig, { ..._, options }); + const options = { ...defaultChartOptions, ..._.options }; + return { ...defaultChartConfig, ..._, options }; }); } - @Injectable() export abstract class PulseChartComponent { - @Input() public translations; - @Input() public charts: Array; - @Input() public shift: number; - @Output() public previous = new EventEmitter(); - @Output() public next = new EventEmitter(); + @Input() + public translations; + @Input() + public charts: PulseChart[]; + @Input() + public shift: number; + @Output() + public previous = new EventEmitter(); + @Output() + public next = new EventEmitter(); public loading = false; public error = false; - constructor(protected pulse: PulseService, protected cd: ChangeDetectorRef) { + constructor(protected pulse: PulseService, protected cd: ChangeDetectorRef) {} + + public resetDatasets() { + if (this.charts) { + this.charts.forEach(c => (c.datasets = [])); + } + this.cd.markForCheck(); } + public abstract update(params, forceUpdate: boolean); + protected setLoading(loading = true) { this.loading = loading; if (this.loading) { @@ -117,7 +122,7 @@ export abstract class PulseChartComponent { this.cd.markForCheck(); } - protected updateDatasets(setId: string, datasets: Array) { + protected updateDatasets(setId: string, datasets: any[]) { if (!this.charts) { return; } @@ -128,13 +133,4 @@ export abstract class PulseChartComponent { c.datasets = datasets; } - - public resetDatasets() { - if (this.charts) { - this.charts.forEach(c => c.datasets = []); - } - this.cd.markForCheck(); - } - - public abstract update(params, forceUpdate: boolean); } diff --git a/src/app/pulse/charts/pulse-cpu-ram-chart/pulse-cpu-ram-chart.component.ts b/src/app/pulse/charts/pulse-cpu-ram-chart/pulse-cpu-ram-chart.component.ts index 26db6a6278..4a1bfcd997 100644 --- a/src/app/pulse/charts/pulse-cpu-ram-chart/pulse-cpu-ram-chart.component.ts +++ b/src/app/pulse/charts/pulse-cpu-ram-chart/pulse-cpu-ram-chart.component.ts @@ -2,36 +2,38 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { forkJoin } from 'rxjs'; import { finalize } from 'rxjs/operators'; -import { humanReadableSize } from '../../unitsUtils'; +import { humanReadableSize } from '../../units-utils'; import { defaultChartOptions, getChart, PulseChartComponent } from '../pulse-chart'; @Component({ selector: 'cs-pulse-cpu-chart', templateUrl: '../pulse-chart.html', - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) -export class PulseCpuRamChartComponent extends PulseChartComponent - implements OnInit { +export class PulseCpuRamChartComponent extends PulseChartComponent implements OnInit { public ngOnInit() { this.charts = getChart([ { id: 'cpu', - options: Object.assign({}, defaultChartOptions, { + options: { + ...defaultChartOptions, scales: { ...defaultChartOptions.scales, - yAxes: [{ - ticks: { - padding: 40, - mirror: true, - suggestedMin: 0, - suggestedMax: 100, - userCallback(val) { - return `${val}%`; - } - } - }] - } - }) + yAxes: [ + { + ticks: { + padding: 40, + mirror: true, + suggestedMin: 0, + suggestedMax: 100, + userCallback(val) { + return `${val}%`; + }, + }, + }, + ], + }, + }, }, { id: 'ram', @@ -39,19 +41,21 @@ export class PulseCpuRamChartComponent extends PulseChartComponent ...defaultChartOptions, scales: { ...defaultChartOptions.scales, - yAxes: [{ - ticks: { - padding: 40, - mirror: true, - suggestedMin: 0, - userCallback(val) { - return humanReadableSize(val * 1024); - } - } - }] - } - } - } + yAxes: [ + { + ticks: { + padding: 40, + mirror: true, + suggestedMin: 0, + userCallback(val) { + return humanReadableSize(val * 1024); + }, + }, + }, + ], + }, + }, + }, ]); } @@ -62,10 +66,10 @@ export class PulseCpuRamChartComponent extends PulseChartComponent { range: params.selectedScale.range, aggregation: _, - shift: `${params.shiftAmount}${params.selectedShift || 'w'}` + shift: `${params.shiftAmount}${params.selectedShift || 'w'}`, }, - forceUpdate - ) + forceUpdate, + ), ); const ramRequests = params.selectedAggregations.map(_ => @@ -74,46 +78,48 @@ export class PulseCpuRamChartComponent extends PulseChartComponent { range: params.selectedScale.range, aggregation: _, - shift: `${params.shiftAmount}${params.selectedShift || 'w'}` + shift: `${params.shiftAmount}${params.selectedShift || 'w'}`, }, - forceUpdate - ) + forceUpdate, + ), ); if (cpuRequests.length) { this.setLoading(!forceUpdate); - forkJoin( - forkJoin(...cpuRequests), - forkJoin(...ramRequests) - ).pipe( - finalize(() => this.setLoading(false))) - .subscribe(([data, ram]) => { - this.error = false; - const datasets = data.map((res: any, ind) => { - const aggregation = params.selectedAggregations[ind]; - return { - data: res.map(_ => ({ - x: new Date(_.time), - y: Math.min(+_.cpuTime, 100) - })), - label: `${this.translations['CPU']} ${aggregation}` - }; - }); - this.updateDatasets('cpu', datasets); + // todo + // tslint:disable-next-line:deprecation + forkJoin(forkJoin(...cpuRequests), forkJoin(...ramRequests)) + .pipe(finalize(() => this.setLoading(false))) + .subscribe( + ([data, ram]) => { + this.error = false; + const datasets = data.map((res: any, ind) => { + const aggregation = params.selectedAggregations[ind]; + return { + data: res.map(_ => ({ + x: new Date(_.time), + y: Math.min(+_.cpuTime, 100), + })), + label: `${this.translations['CPU']} ${aggregation}`, + }; + }); + this.updateDatasets('cpu', datasets); - const asd = ram.map((res: any, ind) => { - const aggregation = params.selectedAggregations[ind]; - return { - data: res.map(_ => ({ - x: new Date(_.time), - y: +_.ram - })), - label: `${this.translations['RAM']} ${aggregation}` - }; - }); - this.updateDatasets('ram', asd); + const asd = ram.map((res: any, ind) => { + const aggregation = params.selectedAggregations[ind]; + return { + data: res.map(_ => ({ + x: new Date(_.time), + y: +_.ram, + })), + label: `${this.translations['RAM']} ${aggregation}`, + }; + }); + this.updateDatasets('ram', asd); - this.cd.markForCheck(); - }, () => (this.error = true)); + this.cd.markForCheck(); + }, + () => (this.error = true), + ); } } } diff --git a/src/app/pulse/charts/pulse-disk-chart/pulse-disk-chart.component.ts b/src/app/pulse/charts/pulse-disk-chart/pulse-disk-chart.component.ts index 6a0423dddd..a9a16131ba 100644 --- a/src/app/pulse/charts/pulse-disk-chart/pulse-disk-chart.component.ts +++ b/src/app/pulse/charts/pulse-disk-chart/pulse-disk-chart.component.ts @@ -5,7 +5,7 @@ import { EventEmitter, Input, OnInit, - Output + Output, } from '@angular/core'; import { forkJoin } from 'rxjs'; import { finalize } from 'rxjs/operators'; @@ -13,27 +13,24 @@ import { finalize } from 'rxjs/operators'; import { isRoot, Volume } from '../../../shared/models/volume.model'; import { VolumeService } from '../../../shared/services/volume.service'; import { PulseService } from '../../pulse.service'; -import { humanReadableSize } from '../../unitsUtils'; +import { humanReadableSize } from '../../units-utils'; import { defaultChartOptions, getChart, PulseChartComponent } from '../pulse-chart'; - @Component({ selector: 'cs-pulse-disk-chart', templateUrl: './pulse-disk-chart.component.html', styleUrls: ['./pulse-disk-chart.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class PulseDiskChartComponent extends PulseChartComponent implements OnInit { - @Input() public vmId: string; - @Output() public volumeChange = new EventEmitter(); + @Input() + public vmId: string; + @Output() + public volumeChange = new EventEmitter(); public selectedVolume: Volume; - public availableVolumes: Array; + public availableVolumes: Volume[]; - constructor( - pulse: PulseService, - cd: ChangeDetectorRef, - private volumeService: VolumeService - ) { + constructor(pulse: PulseService, cd: ChangeDetectorRef, private volumeService: VolumeService) { super(pulse, cd); } @@ -55,97 +52,100 @@ export class PulseDiskChartComponent extends PulseChartComponent implements OnIn return !!humanReadableSize(val, true) ? `${humanReadableSize(val, true)}/s` : null; - } - } - } - ] - } - } + }, + }, + }, + ], + }, + }, }, { id: 'iops' }, { id: 'errors', labels: [], - chartType: 'bar' - } + chartType: 'bar', + }, ]); - this.volumeService - .getList({ virtualMachineId: this.vmId }) - .subscribe(volumes => { - const rootDiskInd = volumes.findIndex(_ => isRoot(_)); - if (rootDiskInd !== -1) { - const temp = volumes[0]; - volumes[0] = volumes[rootDiskInd]; - volumes[rootDiskInd] = temp; - } - this.availableVolumes = volumes; - this.selectedVolume = this.availableVolumes[0]; - this.cd.markForCheck(); - }); + this.volumeService.getList({ virtualMachineId: this.vmId }).subscribe(volumes => { + const rootDiskInd = volumes.findIndex(isRoot); + if (rootDiskInd !== -1) { + const temp = volumes[0]; + volumes[0] = volumes[rootDiskInd]; + volumes[rootDiskInd] = temp; + } + this.availableVolumes = volumes; + this.selectedVolume = this.availableVolumes[0]; + this.cd.markForCheck(); + }); } public update(params, forceUpdate) { const requests = params.selectedAggregations.map(_ => - this.pulse.disk(params.vmId, this.selectedVolume.id, { - range: params.selectedScale.range, - aggregation: _, - shift: `${params.shiftAmount}${params.selectedShift || 'w'}` - }, forceUpdate) + this.pulse.disk( + params.vmId, + this.selectedVolume.id, + { + range: params.selectedScale.range, + aggregation: _, + shift: `${params.shiftAmount}${params.selectedShift || 'w'}`, + }, + forceUpdate, + ), ); this.setLoading(!forceUpdate); - forkJoin(...requests).pipe( - finalize(() => this.setLoading(false))) + // todo + // tslint:disable-next-line:deprecation + forkJoin(...requests) + .pipe(finalize(() => this.setLoading(false))) .subscribe(data => { const sets = { bytes: [], iops: [], - errors: [] + errors: [], }; data.forEach((res: any, ind) => { const aggregation = params.selectedAggregations[ind]; const readBytes = { data: res.map(_ => ({ x: new Date(_.time), - y: +_.readBytes + y: +_.readBytes, })), - label: `${this.translations['DISK_READ']} ${aggregation}` + label: `${this.translations['DISK_READ']} ${aggregation}`, }; const writeBytes = { data: res.map(_ => ({ x: new Date(_.time), - y: +_.writeBytes + y: +_.writeBytes, })), - label: `${this.translations['DISK_WRITE']} ${aggregation}` + label: `${this.translations['DISK_WRITE']} ${aggregation}`, }; sets.bytes.push(readBytes, writeBytes); - const readIops = { data: res.map(_ => ({ x: new Date(_.time), - y: +_.readIOPS + y: +_.readIOPS, })), - label: `${this.translations['DISK_READ_IOPS']} ${aggregation}` + label: `${this.translations['DISK_READ_IOPS']} ${aggregation}`, }; const writeIops = { data: res.map(_ => ({ x: new Date(_.time), - y: +_.writeIOPS + y: +_.writeIOPS, })), - label: `${this.translations['DISK_WRITE_IOPS']} ${aggregation}` + label: `${this.translations['DISK_WRITE_IOPS']} ${aggregation}`, }; sets.iops.push(readIops, writeIops); - const errors = { data: res.map(_ => ({ x: new Date(_.time), - y: +_.ioErrors + y: +_.ioErrors, })), - label: `${this.translations['DISK_IO_ERRORS']} ${aggregation}` + label: `${this.translations['DISK_IO_ERRORS']} ${aggregation}`, }; sets.errors.push(errors); }); diff --git a/src/app/pulse/charts/pulse-network-chart/pulse-network-chart.component.ts b/src/app/pulse/charts/pulse-network-chart/pulse-network-chart.component.ts index ecb8556a44..4d18731943 100644 --- a/src/app/pulse/charts/pulse-network-chart/pulse-network-chart.component.ts +++ b/src/app/pulse/charts/pulse-network-chart/pulse-network-chart.component.ts @@ -5,7 +5,7 @@ import { EventEmitter, Input, OnInit, - Output + Output, } from '@angular/core'; import { forkJoin } from 'rxjs'; import { finalize } from 'rxjs/operators'; @@ -13,27 +13,24 @@ import { finalize } from 'rxjs/operators'; import { NIC } from '../../../shared/models/nic.model'; import { VmService } from '../../../vm/shared/vm.service'; import { PulseService } from '../../pulse.service'; -import { humanReadableSizeInBits } from '../../unitsUtils'; +import { humanReadableSizeInBits } from '../../units-utils'; import { defaultChartOptions, getChart, PulseChartComponent } from '../pulse-chart'; - @Component({ selector: 'cs-pulse-network-chart', templateUrl: './pulse-network-chart.component.html', styleUrls: ['./pulse-network-chart.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class PulseNetworkChartComponent extends PulseChartComponent implements OnInit { - @Input() vmId: string; - @Output() public nicChange = new EventEmitter(); + @Input() + vmId: string; + @Output() + public nicChange = new EventEmitter(); public selectedNic: NIC; - public availableNics: Array = []; + public availableNics: NIC[] = []; - constructor( - pulse: PulseService, - cd: ChangeDetectorRef, - private vmService: VmService - ) { + constructor(pulse: PulseService, cd: ChangeDetectorRef, private vmService: VmService) { super(pulse, cd); } @@ -55,53 +52,58 @@ export class PulseNetworkChartComponent extends PulseChartComponent implements O return !!humanReadableSizeInBits(val) ? `${humanReadableSizeInBits(val)}/s` : null; - } - } - } - ] - } - } + }, + }, + }, + ], + }, + }, }, { id: 'packets' }, { id: 'drops', labels: [], - chartType: 'bar' + chartType: 'bar', }, { id: 'errors', labels: [], - chartType: 'bar' - } + chartType: 'bar', + }, ]); - this.vmService - .get(this.vmId) - .subscribe(vm => { - this.availableNics = vm.nic; - this.selectedNic = this.availableNics[0]; - this.cd.markForCheck(); - }); + this.vmService.get(this.vmId).subscribe(vm => { + this.availableNics = vm.nic; + this.selectedNic = this.availableNics[0]; + this.cd.markForCheck(); + }); } public update(params, forceUpdate) { const requests = params.selectedAggregations.map(_ => - this.pulse.network(this.vmId, this.selectedNic.macaddress, { - range: params.selectedScale.range, - aggregation: _, - shift: `${params.shiftAmount}${params.selectedShift || 'w'}` - }, forceUpdate) + this.pulse.network( + this.vmId, + this.selectedNic.macaddress, + { + range: params.selectedScale.range, + aggregation: _, + shift: `${params.shiftAmount}${params.selectedShift || 'w'}`, + }, + forceUpdate, + ), ); this.setLoading(!forceUpdate); - forkJoin(...requests).pipe( - finalize(() => this.setLoading(false))) + // todo + // tslint:disable-next-line:deprecation + forkJoin(...requests) + .pipe(finalize(() => this.setLoading(false))) .subscribe(data => { const sets = { bits: [], packets: [], drops: [], - errors: [] + errors: [], }; data.forEach((res: any, ind) => { @@ -109,64 +111,64 @@ export class PulseNetworkChartComponent extends PulseChartComponent implements O const readBits = { data: res.map(_ => ({ x: new Date(_.time), - y: +_.readBits + y: +_.readBits, })), - label: `${this.translations['NETWORK_READ']} ${aggregation}` + label: `${this.translations['NETWORK_READ']} ${aggregation}`, }; const writeBits = { data: res.map(_ => ({ x: new Date(_.time), - y: +_.writeBits + y: +_.writeBits, })), - label: `${this.translations['NETWORK_WRITE']} ${aggregation}` + label: `${this.translations['NETWORK_WRITE']} ${aggregation}`, }; sets.bits.push(readBits, writeBits); const readPackets = { data: res.map(_ => ({ x: new Date(_.time), - y: +_.readPackets + y: +_.readPackets, })), - label: `${this.translations['NETWORK_READ_PACKETS']} ${aggregation}` + label: `${this.translations['NETWORK_READ_PACKETS']} ${aggregation}`, }; const writePackets = { data: res.map(_ => ({ x: new Date(_.time), - y: +_.writePackets + y: +_.writePackets, })), - label: `${this.translations['NETWORK_WRITE_PACKETS']} ${aggregation}` + label: `${this.translations['NETWORK_WRITE_PACKETS']} ${aggregation}`, }; sets.packets.push(readPackets, writePackets); const readDrops = { data: res.map(_ => ({ x: new Date(_.time), - y: +_.readDrops + y: +_.readDrops, })), - label: `${this.translations['NETWORK_READ_DROPS']} ${aggregation}` + label: `${this.translations['NETWORK_READ_DROPS']} ${aggregation}`, }; const writeDrops = { data: res.map(_ => ({ x: new Date(_.time), - y: +_.writeDrops + y: +_.writeDrops, })), - label: `${this.translations['NETWORK_WRITE_DROPS']} ${aggregation}` + label: `${this.translations['NETWORK_WRITE_DROPS']} ${aggregation}`, }; sets.drops.push(readDrops, writeDrops); const readErrors = { data: res.map(_ => ({ x: new Date(_.time), - y: +_.readErrors + y: +_.readErrors, })), - label: `${this.translations['NETWORK_READ_ERRORS']} ${aggregation}` + label: `${this.translations['NETWORK_READ_ERRORS']} ${aggregation}`, }; const writeErrors = { data: res.map(_ => ({ x: new Date(_.time), y: +_.writeErrors, })), - label: `${this.translations['NETWORK_WRITE_ERRORS']} ${aggregation}` + label: `${this.translations['NETWORK_WRITE_ERRORS']} ${aggregation}`, }; sets.errors.push(readErrors, writeErrors); }); diff --git a/src/app/pulse/pulse.module.ts b/src/app/pulse/pulse.module.ts index 2c8b832531..a1b6c63e93 100644 --- a/src/app/pulse/pulse.module.ts +++ b/src/app/pulse/pulse.module.ts @@ -4,7 +4,11 @@ import { NgModule } from '@angular/core'; import { SharedModule } from '../shared/shared.module'; import { MaterialModule } from '../material/material.module'; -import { PulseCpuRamChartComponent, PulseDiskChartComponent, PulseNetworkChartComponent, } from './charts/'; +import { + PulseCpuRamChartComponent, + PulseDiskChartComponent, + PulseNetworkChartComponent, +} from './charts/'; import { AggregationSelectorComponent } from './charts/aggregation-selector.component'; import { ChartAreaComponent } from './charts/chart-area.component'; import { PulseService } from './pulse.service'; @@ -12,15 +16,8 @@ import { VmPulseComponent } from './vm-pulse/vm-pulse.component'; import { BaseChartDirective } from './charts/chart.directive'; @NgModule({ - imports: [ - CommonModule, - SharedModule, - MaterialModule - ], - exports: [ - BaseChartDirective, - VmPulseComponent - ], + imports: [CommonModule, SharedModule, MaterialModule], + exports: [BaseChartDirective, VmPulseComponent], providers: [PulseService], declarations: [ BaseChartDirective, @@ -29,9 +26,8 @@ import { BaseChartDirective } from './charts/chart.directive'; PulseCpuRamChartComponent, PulseNetworkChartComponent, PulseDiskChartComponent, - VmPulseComponent + VmPulseComponent, ], - entryComponents: [VmPulseComponent] + entryComponents: [VmPulseComponent], }) -export class PulseModule { -} +export class PulseModule {} diff --git a/src/app/pulse/pulse.service.ts b/src/app/pulse/pulse.service.ts index 299f078b47..f3f4475e47 100644 --- a/src/app/pulse/pulse.service.ts +++ b/src/app/pulse/pulse.service.ts @@ -11,33 +11,23 @@ interface TimeParams { shift: string; } - export interface Interval { scales: Object; } @Injectable() export class PulseService { - constructor(protected http: HttpClient) { - } + constructor(protected http: HttpClient) {} public getPermittedIntervals() { return this.http.get(`cs-extensions/pulse/permitted-intervals`); } - public cpuTime( - vmId: string, - params: TimeParams, - forceUpdate = false - ): Observable> { + public cpuTime(vmId: string, params: TimeParams, forceUpdate = false): Observable { return this.request('cputime', vmId, params, forceUpdate); } - public ram( - vmId: string, - params: TimeParams, - forceUpdate = false - ): Observable> { + public ram(vmId: string, params: TimeParams, forceUpdate = false): Observable { return this.request('ram', vmId, params, forceUpdate); } @@ -45,8 +35,8 @@ export class PulseService { vmId: string, diskId: string, params: TimeParams, - forceUpdate = false - ): Observable> { + forceUpdate = false, + ): Observable { return this.request('disk', `${vmId}/${diskId}`, params, forceUpdate); } @@ -54,22 +44,12 @@ export class PulseService { vmId: string, macAddress: string, params: TimeParams, - forceUpdate = false - ): Observable> { - return this.request( - 'network-interface', - `${vmId}/${macAddress}`, - params, - forceUpdate - ); + forceUpdate = false, + ): Observable { + return this.request('network-interface', `${vmId}/${macAddress}`, params, forceUpdate); } - protected request( - endpoint: string, - params: string, - timeParams: TimeParams, - forceUpdate = false - ) { + protected request(endpoint: string, params: string, timeParams: TimeParams, forceUpdate = false) { const t = `${timeParams.range}/${timeParams.aggregation}/${timeParams.shift}`; let requestParams = new HttpParams(); @@ -78,7 +58,7 @@ export class PulseService { } return this.http - .get(`cs-extensions/pulse/${endpoint}/${params}/${t}`, { params: requestParams }).pipe( - map(res => res['result'])); + .get(`cs-extensions/pulse/${endpoint}/${params}/${t}`, { params: requestParams }) + .pipe(map(res => res['result'])); } } diff --git a/src/app/pulse/unitsUtils.ts b/src/app/pulse/units-utils.ts similarity index 58% rename from src/app/pulse/unitsUtils.ts rename to src/app/pulse/units-utils.ts index d3b4797e32..fa4edd096f 100644 --- a/src/app/pulse/unitsUtils.ts +++ b/src/app/pulse/units-utils.ts @@ -1,32 +1,24 @@ const bytes = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; const siBytes = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; -const siBits = [ - 'bits', - 'kbits', - 'Mbits', - 'Gbits', - 'Tbits', - 'Pbits', - 'Ebits', - 'Zbits', - 'Ybits' -]; +const siBits = ['bits', 'kbits', 'Mbits', 'Gbits', 'Tbits', 'Pbits', 'Ebits', 'Zbits', 'Ybits']; -function getSize(size: number, units: Array, kB: number) { +function getSize(size: number, units: string[], kB: number) { let u = 0; if (Math.abs(size) < kB) { if (Math.abs(size) <= 1) { - return (Math.abs(size) % 1 === 0) ? `${size.toFixed(0)} ${units[u]}` : null; + return Math.abs(size) % 1 === 0 ? `${size.toFixed(0)} ${units[u]}` : null; } return `${size.toFixed(1)} ${units[u]}`; } + + let localSize = size; do { - size /= kB; - ++u; - } while (Math.abs(size) >= kB && u < units.length - 1); + localSize /= kB; + u += 1; + } while (Math.abs(localSize) >= kB && u < units.length - 1); - return `${size.toFixed(1)} ${units[u]}`; + return `${localSize.toFixed(1)} ${units[u]}`; } // size in bytes diff --git a/src/app/pulse/vm-pulse/vm-pulse.component.ts b/src/app/pulse/vm-pulse/vm-pulse.component.ts index af1f787248..66f9887818 100644 --- a/src/app/pulse/vm-pulse/vm-pulse.component.ts +++ b/src/app/pulse/vm-pulse/vm-pulse.component.ts @@ -4,7 +4,7 @@ import { Inject, OnDestroy, OnInit, - ViewChild + ViewChild, } from '@angular/core'; import { MAT_DIALOG_DATA, MatTabChangeEvent } from '@angular/material'; import { TranslateService } from '@ngx-translate/core'; @@ -20,10 +20,10 @@ import { Interval, PulseService } from '../pulse.service'; const enum TabIndex { CpuRam, Network, - Disk + Disk, } -export const PulseParameters = { +export const pulseParameters = { ScaleRange: 'pulseScaleRange', Aggregations: 'pulseAggregations', Shift: 'pulseShift', @@ -34,22 +34,27 @@ export const PulseParameters = { selector: 'cs-vm-pulse', templateUrl: './vm-pulse.component.html', styleUrls: ['./vm-pulse.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, }) export class VmPulseComponent implements OnInit, OnDestroy { - @ViewChild(PulseCpuRamChartComponent) cpuRamChart: PulseCpuRamChartComponent; - @ViewChild(PulseNetworkChartComponent) networkChart: PulseNetworkChartComponent; - @ViewChild(PulseDiskChartComponent) diskChart: PulseDiskChartComponent; + @ViewChild(PulseCpuRamChartComponent) + cpuRamChart: PulseCpuRamChartComponent; + @ViewChild(PulseNetworkChartComponent) + networkChart: PulseNetworkChartComponent; + @ViewChild(PulseDiskChartComponent) + diskChart: PulseDiskChartComponent; public tabIndex = 0; public permittedIntervals; public translations; + // tslint:disable:variable-name private _selectedScale; private _selectedAggregations = []; private _selectedShift; private _shiftAmount = 0; + // tslint:enable:variable-name private updateInterval; @@ -57,7 +62,7 @@ export class VmPulseComponent implements OnInit, OnDestroy { @Inject(MAT_DIALOG_DATA) public vmId: string, private pulse: PulseService, private translateService: TranslateService, - private storage: LocalStorageService + private storage: LocalStorageService, ) { this.updateChart = debounce(this.updateChart, 300); } @@ -89,18 +94,15 @@ export class VmPulseComponent implements OnInit, OnDestroy { this._selectedScale = value; if (this.selectedScale) { - this.storage.write(PulseParameters.ScaleRange, this._selectedScale.range); + this.storage.write(pulseParameters.ScaleRange, this._selectedScale.range); const available = this.selectedAggregations.reduce((res, val) => { - return this._selectedScale.aggregations.find((a) => a === val) - ? res.concat(val) : res; + return this._selectedScale.aggregations.find(a => a === val) ? res.concat(val) : res; }, []); - if (!!available.length) { - this.selectedAggregations = available; - } else { - this.selectedAggregations = [this._selectedScale.aggregations[0]]; - } + this.selectedAggregations = !!available.length + ? available + : [this._selectedScale.aggregations[0]]; } } @@ -111,18 +113,13 @@ export class VmPulseComponent implements OnInit, OnDestroy { public set selectedAggregations(value) { this._selectedAggregations = value; - this.storage.write( - PulseParameters.Aggregations, - this._selectedAggregations.toString() - ); + this.storage.write(pulseParameters.Aggregations, this._selectedAggregations.toString()); if (Array.isArray(value) && !value.length) { this.resetDatasets(); } else if (value) { this.updateChart(); } - - } public get selectedShift() { @@ -131,7 +128,7 @@ export class VmPulseComponent implements OnInit, OnDestroy { public set selectedShift(value) { this._selectedShift = value; - this.storage.write(PulseParameters.Shift, this._selectedShift); + this.storage.write(pulseParameters.Shift, this._selectedShift); // TODO if (this.shouldUpdate()) { @@ -145,7 +142,7 @@ export class VmPulseComponent implements OnInit, OnDestroy { public set shiftAmount(value) { this._shiftAmount = value; - this.storage.write(PulseParameters.ShiftAmount, this._shiftAmount.toString()); + this.storage.write(pulseParameters.ShiftAmount, this._shiftAmount.toString()); if (this.shouldUpdate()) { this.updateChart(this.tabIndex); @@ -167,12 +164,12 @@ export class VmPulseComponent implements OnInit, OnDestroy { } public handlePrevious() { - this.shiftAmount++; + this.shiftAmount += 1; this.updateChart(); } public handleNext() { - this.shiftAmount--; + this.shiftAmount -= 1; this.updateChart(); } @@ -189,16 +186,15 @@ export class VmPulseComponent implements OnInit, OnDestroy { selectedAggregations: this.selectedAggregations, selectedScale: this.selectedScale, selectedShift: this.selectedShift, - shiftAmount: this.shiftAmount + shiftAmount: this.shiftAmount, }, - forceUpdate + forceUpdate, ); } - } private resetDatasets() { - for (let i = TabIndex.CpuRam; i < TabIndex.Disk; i++) { + for (let i = TabIndex.CpuRam; i < TabIndex.Disk; i += 1) { const chart = this.getChart(i); if (chart) { chart.resetDatasets(); @@ -232,31 +228,31 @@ export class VmPulseComponent implements OnInit, OnDestroy { this.refresh(); } - private getScale(): { range: string, aggregations: string[] } { - const scale = this.storage.read(PulseParameters.ScaleRange); - return (!!scale) + private getScale(): { range: string; aggregations: string[] } { + const scale = this.storage.read(pulseParameters.ScaleRange); + return !!scale ? this.permittedIntervals.scales.find(_ => _.range === scale) : this.permittedIntervals.scales[0]; } private getAggregations(): string[] { - const aggregations = this.storage.read(PulseParameters.Aggregations); + const aggregations = this.storage.read(pulseParameters.Aggregations); if (aggregations) { return aggregations.split(','); - } else if (this._selectedScale && !!this._selectedScale.aggregations.length) { + } + if (this._selectedScale && !!this._selectedScale.aggregations.length) { return [this._selectedScale.aggregations[0]]; - } else { - return []; } + return []; } private getShift(): string { - const shift = this.storage.read(PulseParameters.Shift); - return (!!shift) ? shift : this.permittedIntervals.shifts[0]; + const shift = this.storage.read(pulseParameters.Shift); + return !!shift ? shift : this.permittedIntervals.shifts[0]; } private getShiftAmount(): number { - const shiftAmount = this.storage.read(PulseParameters.ShiftAmount); - return (shiftAmount) ? +shiftAmount : 0; + const shiftAmount = this.storage.read(pulseParameters.ShiftAmount); + return shiftAmount ? +shiftAmount : 0; } } diff --git a/src/app/reducers/account-tags/redux/account-tags.actions.ts b/src/app/reducers/account-tags/redux/account-tags.actions.ts index c276439fba..3958f66173 100644 --- a/src/app/reducers/account-tags/redux/account-tags.actions.ts +++ b/src/app/reducers/account-tags/redux/account-tags.actions.ts @@ -4,21 +4,16 @@ import { Tag } from '../../../shared/models'; export const LOAD_ACCOUNT_TAGS_REQUEST = '[ACCOUNT_TAGS] LOAD_ACCOUNT_TAGS_REQUEST'; export const LOAD_ACCOUNT_TAGS_RESPONSE = '[ACCOUNT_TAGS] LOAD_ACCOUNT_TAGS_RESPONSE'; - export class LoadAccountTagsRequest implements Action { readonly type = LOAD_ACCOUNT_TAGS_REQUEST; - constructor(public payload?: any) { - } + constructor(public payload?: any) {} } export class LoadAccountTagsResponse implements Action { readonly type = LOAD_ACCOUNT_TAGS_RESPONSE; - constructor(readonly payload: Tag[]) { - } + constructor(readonly payload: Tag[]) {} } -export type Actions = - | LoadAccountTagsRequest - | LoadAccountTagsResponse; +export type Actions = LoadAccountTagsRequest | LoadAccountTagsResponse; diff --git a/src/app/reducers/account-tags/redux/account-tags.effects.spec.ts b/src/app/reducers/account-tags/redux/account-tags.effects.spec.ts index a2b37ba4c2..99fbffee2a 100644 --- a/src/app/reducers/account-tags/redux/account-tags.effects.spec.ts +++ b/src/app/reducers/account-tags/redux/account-tags.effects.spec.ts @@ -14,51 +14,10 @@ import { Tag } from '../../../shared/models'; import { TagService } from '../../../shared/services/tags/tag.service'; import { AccountTagService } from '../../../shared/services/tags/account-tag.service'; -@Injectable() -class MockAsyncJobService { - public completeAllJobs(): void { - } -} - @Injectable() class MockTagService { - public getList(): void { - } - public setServiceOfferingParams(): void { - } -} - - -@Injectable() -class MockStorageService { - private storage: any = { - user: { - userid: '1' - } - }; - - public write(key: string, value: string): void { - this.storage[key] = value; - } - - public read(key: string): string { - return this.storage[key] || null; - } - - public remove(key: string): void { - delete this.storage[key]; - } - - public resetInMemoryStorage(): void { - this.storage = {}; - } -} - -class MockMatDialog { - public open(): void { - } - public closeAll(): void { - } + public getList(): void {} + public setServiceOfferingParams(): void {} } export class TestActions extends Actions { @@ -67,6 +26,8 @@ export class TestActions extends Actions { } public set stream(source: Observable) { + // todo + // tslint:disable-next-line this.source = source; } } @@ -75,23 +36,19 @@ export function getActions() { return new TestActions(); } - describe('Account tags Effects', () => { let actions$: TestActions; let service: TagService; - let accountService: AccountTagService; - let dialogService: DialogService; let effects: AccountTagsEffects; - const list: Array = []; - + const list: Tag[] = []; beforeEach(() => { TestBed.configureTestingModule({ imports: [ HttpClientTestingModule, - StoreModule.forRoot({ ...fromAccountTags.accountTagsReducers}), + StoreModule.forRoot({ ...fromAccountTags.accountTagsReducers }), ], providers: [ AccountTagsEffects, @@ -99,12 +56,10 @@ describe('Account tags Effects', () => { { provide: TagService, useClass: MockTagService }, { provide: AccountTagService, useClass: MockTagService }, { provide: DialogService, useClass: MockDialogService }, - ] + ], }); actions$ = TestBed.get(Actions); service = TestBed.get(TagService); - accountService = TestBed.get(AccountTagService); - dialogService = TestBed.get(DialogService); effects = TestBed.get(AccountTagsEffects); }); @@ -122,8 +77,7 @@ describe('Account tags Effects', () => { }); it('should return an empty collection from LoadAccountTagsResponse', () => { - const spyGetList = spyOn(service, 'getList').and - .returnValue(throwError(new Error('Error occurred!'))); + spyOn(service, 'getList').and.returnValue(throwError(new Error('Error occurred!'))); const action = new accountTagActions.LoadAccountTagsRequest(); const completion = new accountTagActions.LoadAccountTagsResponse([]); diff --git a/src/app/reducers/account-tags/redux/account-tags.effects.ts b/src/app/reducers/account-tags/redux/account-tags.effects.ts index fc00f12152..d06d81cc6f 100644 --- a/src/app/reducers/account-tags/redux/account-tags.effects.ts +++ b/src/app/reducers/account-tags/redux/account-tags.effects.ts @@ -15,12 +15,10 @@ export class AccountTagsEffects { switchMap((action: actions.LoadAccountTagsRequest) => { return this.tagService.getList(action.payload).pipe( map(tags => new actions.LoadAccountTagsResponse(tags)), - catchError(() => of(new actions.LoadAccountTagsResponse([])))); - })); + catchError(() => of(new actions.LoadAccountTagsResponse([]))), + ); + }), + ); - constructor( - private actions$: Actions, - private tagService: TagService - ) { - } + constructor(private actions$: Actions, private tagService: TagService) {} } diff --git a/src/app/reducers/account-tags/redux/account-tags.reducers.ts b/src/app/reducers/account-tags/redux/account-tags.reducers.ts index 181743b0cf..07e37a2b5b 100644 --- a/src/app/reducers/account-tags/redux/account-tags.reducers.ts +++ b/src/app/reducers/account-tags/redux/account-tags.reducers.ts @@ -4,7 +4,6 @@ import { Tag } from '../../../shared/models/tag.model'; import * as accountTagActions from './account-tags.actions'; - /** * @ngrx/entity provides a predefined interface for handling * a structured dictionary of records. This interface @@ -13,7 +12,7 @@ import * as accountTagActions from './account-tags.actions'; * any additional interface properties. */ export interface State extends EntityState { - loading: boolean, + loading: boolean; } export interface AccountTagsState { @@ -34,7 +33,7 @@ export const accountTagsReducers = { */ export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: Tag) => item.key, - sortComparer: false + sortComparer: false, }); /** getInitialState returns the default initial state @@ -42,18 +41,15 @@ export const adapter: EntityAdapter = createEntityAdapter({ * additional properties can also be defined. */ export const initialState: State = adapter.getInitialState({ - loading: false + loading: false, }); -export function reducer( - state = initialState, - action: accountTagActions.Actions -): State { +export function reducer(state = initialState, action: accountTagActions.Actions): State { switch (action.type) { case accountTagActions.LOAD_ACCOUNT_TAGS_REQUEST: { return { ...state, - loading: true + loading: true, }; } case accountTagActions.LOAD_ACCOUNT_TAGS_RESPONSE: { @@ -66,7 +62,7 @@ export function reducer( * sort each record upon entry into the sorted array. */ ...adapter.addAll(action.payload, state), - loading: false + loading: false, }; } default: { @@ -75,22 +71,12 @@ export function reducer( } } - export const getAccountTagsState = createFeatureSelector('tags'); -export const getAccountTagsEntitiesState = createSelector( - getAccountTagsState, - state => state.list -); +export const getAccountTagsEntitiesState = createSelector(getAccountTagsState, state => state.list); -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getAccountTagsEntitiesState); - -export const isLoading = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getAccountTagsEntitiesState, - state => state.loading ); + +export const isLoading = createSelector(getAccountTagsEntitiesState, state => state.loading); diff --git a/src/app/reducers/accounts/redux/accounts.actions.ts b/src/app/reducers/accounts/redux/accounts.actions.ts index c36eb0d343..ca4c40374c 100644 --- a/src/app/reducers/accounts/redux/accounts.actions.ts +++ b/src/app/reducers/accounts/redux/accounts.actions.ts @@ -32,159 +32,137 @@ export const ACCOUNT_LOAD_USER_KEYS_SUCCESS = '[ACCOUNTS] ACCOUNT_LOAD_USER_KEYS export class LoadAccountsRequest implements Action { type = LOAD_ACCOUNTS_REQUEST; - constructor(public payload?: any) { - } + constructor(public payload?: any) {} } export class LoadAccountsResponse implements Action { type = LOAD_ACCOUNTS_RESPONSE; - constructor(public payload: Account[]) { - } + constructor(public payload: Account[]) {} } export class LoadSelectedAccount implements Action { type = LOAD_SELECTED_ACCOUNT; - constructor(public payload: string) { - } + constructor(public payload: string) {} } export class AccountFilterUpdate implements Action { type = ACCOUNT_FILTER_UPDATE; - constructor(public payload: { [key: string]: any }) { - } + constructor(public payload: { [key: string]: any }) {} } export class CreateAccount implements Action { readonly type = CREATE_ACCOUNT; - constructor(public payload: AccountData) { - } + constructor(public payload: AccountData) {} } export class CreateSuccess implements Action { readonly type = ACCOUNT_CREATE_SUCCESS; - constructor(public payload: Account) { - } + constructor(public payload: Account) {} } export class CreateError implements Action { readonly type = ACCOUNT_CREATE_ERROR; - constructor(public payload: Error) { - } + constructor(public payload: Error) {} } export class UpdateAccount implements Action { readonly type = UPDATE_ACCOUNT; - constructor(public payload: Account) { - } + constructor(public payload: Account) {} } export class AccountUpdateError implements Action { readonly type = ACCOUNT_UPDATE_ERROR; - constructor(public payload: Error) { - } + constructor(public payload: Error) {} } export class EnableAccountRequest implements Action { readonly type = ENABLE_ACCOUNT; - constructor(public payload: Account) { - } + constructor(public payload: Account) {} } export class DisableAccountRequest implements Action { readonly type = DISABLE_ACCOUNT; - constructor(public payload: Account) { - } + constructor(public payload: Account) {} } export class DeleteAccountRequest implements Action { readonly type = DELETE_ACCOUNT; - constructor(public payload: Account) { - } + constructor(public payload: Account) {} } export class DeleteSuccess implements Action { readonly type = ACCOUNT_DELETE_SUCCESS; - constructor(public payload: Account) { - } + constructor(public payload: Account) {} } export class AccountUserCreate implements Action { readonly type = ACCOUNT_USER_CREATE; - constructor(public payload: AccountUser) { - } + constructor(public payload: AccountUser) {} } export class AccountUserCreateSuccess implements Action { readonly type = ACCOUNT_USER_CREATE_SUCCESS; - constructor(public payload: AccountUser) { - } + constructor(public payload: AccountUser) {} } export class AccountUserUpdate implements Action { readonly type = ACCOUNT_USER_UPDATE; - constructor(public payload: AccountUser) { - } + constructor(public payload: AccountUser) {} } export class AccountUserUpdateSuccess implements Action { readonly type = ACCOUNT_USER_UPDATE_SUCCESS; - constructor(public payload: AccountUser) { - } + constructor(public payload: AccountUser) {} } export class AccountUserDelete implements Action { readonly type = ACCOUNT_USER_DELETE; - constructor(public payload: AccountUser) { - } + constructor(public payload: AccountUser) {} } export class AccountUserDeleteSuccess implements Action { readonly type = ACCOUNT_USER_DELETE_SUCCESS; - constructor(public payload: AccountUser) { - } + constructor(public payload: AccountUser) {} } export class AccountUserGenerateKey implements Action { readonly type = ACCOUNT_USER_GENERATE_KEYS; - constructor(public payload: AccountUser) { - } + constructor(public payload: AccountUser) {} } export class AccountLoadUserKeys implements Action { readonly type = ACCOUNT_LOAD_USER_KEYS; - constructor(public payload: AccountUser) { - } + constructor(public payload: AccountUser) {} } export class AccountLoadUserKeysSuccess implements Action { readonly type = ACCOUNT_LOAD_USER_KEYS_SUCCESS; - constructor(public payload: { user: AccountUser, userKeys: ApiKeys }) { - } + constructor(public payload: { user: AccountUser; userKeys: ApiKeys }) {} } - -export type Actions = LoadAccountsRequest +export type Actions = + | LoadAccountsRequest | LoadAccountsResponse | AccountFilterUpdate | LoadSelectedAccount diff --git a/src/app/reducers/accounts/redux/accounts.effects.ts b/src/app/reducers/accounts/redux/accounts.effects.ts index c2ffae66ce..807f6851c2 100644 --- a/src/app/reducers/accounts/redux/accounts.effects.ts +++ b/src/app/reducers/accounts/redux/accounts.effects.ts @@ -20,7 +20,6 @@ import * as accountActions from './accounts.actions'; @Injectable() export class AccountsEffects { - @Effect() loadAccounts$: Observable = this.actions$.pipe( ofType(accountActions.LOAD_ACCOUNTS_REQUEST), @@ -29,8 +28,10 @@ export class AccountsEffects { map((accounts: Account[]) => { return new accountActions.LoadAccountsResponse(accounts); }), - catchError(() => of(new accountActions.LoadAccountsResponse([])))); - })); + catchError(() => of(new accountActions.LoadAccountsResponse([]))), + ); + }), + ); @Effect() updateAccounts$: Observable = this.actions$.pipe( @@ -41,17 +42,20 @@ export class AccountsEffects { volumeActions.VOLUME_CREATE_SUCCESS, volumeActions.RESIZE_VOLUME_SUCCESS, snapshotActions.ADD_SNAPSHOT_SUCCESS, - snapshotActions.DELETE_SNAPSHOT_SUCCESS + snapshotActions.DELETE_SNAPSHOT_SUCCESS, ), map(() => { return new accountActions.LoadAccountsRequest(); - })); + }), + ); @Effect() disableAccount$: Observable = this.actions$.pipe( ofType(accountActions.DISABLE_ACCOUNT), mergeMap((action: accountActions.DisableAccountRequest) => { - const notificationId = this.jobsNotificationService.add('NOTIFICATIONS.ACCOUNT.DISABLE_IN_PROGRESS'); + const notificationId = this.jobsNotificationService.add( + 'NOTIFICATIONS.ACCOUNT.DISABLE_IN_PROGRESS', + ); return this.accountService.disableAccount(action.payload).pipe( tap(() => { const message = 'NOTIFICATIONS.ACCOUNT.DISABLE_DONE'; @@ -62,8 +66,10 @@ export class AccountsEffects { const message = 'NOTIFICATIONS.ACCOUNT.DISABLE_FAILED'; this.showNotificationsOnFail(error, message, notificationId); return of(new accountActions.AccountUpdateError(error)); - })); - })); + }), + ); + }), + ); @Effect() enableAccount$: Observable = this.actions$.pipe( @@ -78,14 +84,18 @@ export class AccountsEffects { catchError((error: Error) => { this.showNotificationsOnFail(error); return of(new accountActions.AccountUpdateError(error)); - })); - })); + }), + ); + }), + ); @Effect() deleteAccount$: Observable = this.actions$.pipe( ofType(accountActions.DELETE_ACCOUNT), mergeMap((action: accountActions.DeleteAccountRequest) => { - const notificationId = this.jobsNotificationService.add('NOTIFICATIONS.ACCOUNT.DELETION_IN_PROGRESS'); + const notificationId = this.jobsNotificationService.add( + 'NOTIFICATIONS.ACCOUNT.DELETION_IN_PROGRESS', + ); return this.accountService.removeAccount(action.payload).pipe( tap(() => { const message = 'NOTIFICATIONS.ACCOUNT.DELETION_DONE'; @@ -96,9 +106,10 @@ export class AccountsEffects { const message = 'NOTIFICATIONS.ACCOUNT.DELETION_FAILED'; this.showNotificationsOnFail(error, message, notificationId); return of(new accountActions.AccountUpdateError(error)); - })); - })); - + }), + ); + }), + ); @Effect() createAccount$: Observable = this.actions$.pipe( @@ -113,15 +124,18 @@ export class AccountsEffects { catchError((error: Error) => { this.showNotificationsOnFail(error); return of(new accountActions.CreateError(error)); - })); - })); + }), + ); + }), + ); @Effect({ dispatch: false }) accountCreateSuccess$: Observable = this.actions$.pipe( ofType(accountActions.ACCOUNT_CREATE_SUCCESS), tap((action: accountActions.CreateSuccess) => { this.dialog.closeAll(); - })); + }), + ); @Effect({ dispatch: false }) deleteSuccessNavigate$: Observable = this.actions$.pipe( @@ -132,9 +146,10 @@ export class AccountsEffects { }), tap(() => { this.router.navigate(['./accounts'], { - queryParamsHandling: 'preserve' + queryParamsHandling: 'preserve', }); - })); + }), + ); @Effect() userDelete$: Observable = this.actions$.pipe( @@ -149,7 +164,10 @@ export class AccountsEffects { catchError(error => { this.showNotificationsOnFail(error); return of(new accountActions.AccountUpdateError(error)); - })))); + }), + ), + ), + ); @Effect() userCreate$: Observable = this.actions$.pipe( @@ -160,18 +178,22 @@ export class AccountsEffects { const message = 'NOTIFICATIONS.ACCOUNT.USER.CREATION_DONE'; this.showNotificationsOnFinish(message); }), - map((user) => new accountActions.AccountUserCreateSuccess(user)), + map(user => new accountActions.AccountUserCreateSuccess(user)), catchError(error => { this.showNotificationsOnFail(error); - return of(new accountActions.AccountUpdateError(error)) - })))); + return of(new accountActions.AccountUpdateError(error)); + }), + ), + ), + ); @Effect({ dispatch: false }) userCreateSuccess$: Observable = this.actions$.pipe( ofType(accountActions.ACCOUNT_USER_CREATE_SUCCESS), tap(() => { this.dialog.closeAll(); - })); + }), + ); @Effect() userUpdate$: Observable = this.actions$.pipe( @@ -182,11 +204,14 @@ export class AccountsEffects { const message = 'NOTIFICATIONS.ACCOUNT.USER.UPDATE_DONE'; this.showNotificationsOnFinish(message); }), - map((user) => new accountActions.AccountUserUpdateSuccess(user)), + map(user => new accountActions.AccountUserUpdateSuccess(user)), catchError(error => { this.showNotificationsOnFail(error); - return of(new accountActions.AccountUpdateError(error)) - })))); + return of(new accountActions.AccountUpdateError(error)); + }), + ), + ), + ); @Effect() userGenerateKeys$: Observable = this.actions$.pipe( @@ -197,25 +222,37 @@ export class AccountsEffects { const message = 'NOTIFICATIONS.ACCOUNT.USER.GENERATE_KEYS_DONE'; this.showNotificationsOnFinish(message); }), - map(res => new accountActions.AccountLoadUserKeysSuccess({ - user: action.payload, - userKeys: res - })), + map( + res => + new accountActions.AccountLoadUserKeysSuccess({ + user: action.payload, + userKeys: res, + }), + ), catchError(error => { this.showNotificationsOnFail(error); - return of(new accountActions.AccountUpdateError(error)) - })))); + return of(new accountActions.AccountUpdateError(error)); + }), + ), + ), + ); @Effect() userLoadKeys$: Observable = this.actions$.pipe( ofType(accountActions.ACCOUNT_LOAD_USER_KEYS), switchMap((action: accountActions.AccountLoadUserKeys) => this.userService.getUserKeys(action.payload.id).pipe( - map(res => new accountActions.AccountLoadUserKeysSuccess({ - user: action.payload, - userKeys: res - })), - catchError(error => of(new accountActions.AccountUpdateError(error)))))); + map( + res => + new accountActions.AccountLoadUserKeysSuccess({ + user: action.payload, + userKeys: res, + }), + ), + catchError(error => of(new accountActions.AccountUpdateError(error))), + ), + ), + ); constructor( private actions$: Actions, @@ -225,15 +262,14 @@ export class AccountsEffects { private dialog: MatDialog, private dialogService: DialogService, private snackBarService: SnackBarService, - private jobsNotificationService: JobsNotificationService - ) { - } + private jobsNotificationService: JobsNotificationService, + ) {} private showNotificationsOnFinish(message: string, jobNotificationId?: string) { if (jobNotificationId) { this.jobsNotificationService.finish({ + message, id: jobNotificationId, - message }); } this.snackBarService.open(message).subscribe(); @@ -242,15 +278,15 @@ export class AccountsEffects { private showNotificationsOnFail(error: any, message?: string, jobNotificationId?: string) { if (jobNotificationId) { this.jobsNotificationService.fail({ + message, id: jobNotificationId, - message }); } this.dialogService.alert({ message: { translationToken: error.message, - interpolateParams: error.params - } + interpolateParams: error.params, + }, }); } } diff --git a/src/app/reducers/accounts/redux/accounts.reducers.ts b/src/app/reducers/accounts/redux/accounts.reducers.ts index ef08b4e91b..a684d895d8 100644 --- a/src/app/reducers/accounts/redux/accounts.reducers.ts +++ b/src/app/reducers/accounts/redux/accounts.reducers.ts @@ -14,15 +14,15 @@ import * as accountActions from './accounts.actions'; * any additional interface properties. */ export interface State extends EntityState { - loading: boolean, + loading: boolean; selectedAccountId: string | null; filters: { - selectedDomainIds: string[], - selectedRoleNames: string[], - selectedRoleTypes: string[], - selectedStates: string[], - selectedGroupings: any[] - } + selectedDomainIds: string[]; + selectedRoleNames: string[]; + selectedRoleTypes: string[]; + selectedStates: string[]; + selectedGroupings: any[]; + }; } export interface AccountsState { @@ -43,7 +43,7 @@ export const accountReducers = { */ export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: Account) => item.id, - sortComparer: Utils.sortByName + sortComparer: Utils.sortByName, }); /** getInitialState returns the default initial state @@ -58,19 +58,16 @@ export const initialState: State = adapter.getInitialState({ selectedRoleTypes: [], selectedRoleNames: [], selectedStates: [], - selectedGroupings: [] - } + selectedGroupings: [], + }, }); -export function reducer( - state = initialState, - action: accountActions.Actions -): State { +export function reducer(state = initialState, action: accountActions.Actions): State { switch (action.type) { case accountActions.LOAD_ACCOUNTS_REQUEST: { return { ...state, - loading: true + loading: true, }; } case accountActions.ACCOUNT_FILTER_UPDATE: { @@ -78,12 +75,11 @@ export function reducer( ...state, filters: { ...state.filters, - ...action.payload - } + ...action.payload, + }, }; } case accountActions.LOAD_ACCOUNTS_RESPONSE: { - const accounts = action.payload; return { @@ -95,14 +91,14 @@ export function reducer( * sort each record upon entry into the sorted array. */ ...adapter.addAll([...accounts], state), - loading: false + loading: false, }; } case accountActions.LOAD_SELECTED_ACCOUNT: { return { ...state, - selectedAccountId: action.payload + selectedAccountId: action.payload, }; } @@ -126,13 +122,9 @@ export function reducer( case accountActions.ACCOUNT_USER_CREATE_SUCCESS: { if (state.entities[action.payload.accountid]) { const users = [...state.entities[action.payload.accountid].user, action.payload]; - return adapter.updateOne( - { id: action.payload.accountid, changes: { user: users } }, - state - ); - } else { - return adapter.addOne(action.payload, state); + return adapter.updateOne({ id: action.payload.accountid, changes: { user: users } }, state); } + return adapter.addOne(action.payload, state); } case accountActions.ACCOUNT_LOAD_USER_KEYS_SUCCESS: { const updatedUser: AccountUser = { ...action.payload.user }; @@ -140,37 +132,30 @@ export function reducer( updatedUser.apikey = action.payload.userKeys.apikey; const users = [ - ...state.entities[action.payload.user.accountid].user - .filter(_ => _.id !== action.payload.user.id), - updatedUser + ...state.entities[action.payload.user.accountid].user.filter( + _ => _.id !== action.payload.user.id, + ), + updatedUser, ]; return adapter.updateOne( { id: action.payload.user.accountid, changes: { user: users } }, - state + state, ); } case accountActions.ACCOUNT_USER_UPDATE_SUCCESS: { const users = [ - ...state.entities[action.payload.accountid].user - .filter(_ => _.id !== action.payload.id), - action.payload + ...state.entities[action.payload.accountid].user.filter(_ => _.id !== action.payload.id), + action.payload, ]; - return adapter.updateOne( - { id: action.payload.accountid, changes: { user: users } }, - state - ); + return adapter.updateOne({ id: action.payload.accountid, changes: { user: users } }, state); } case accountActions.ACCOUNT_USER_DELETE_SUCCESS: { const users = [ - ...state.entities[action.payload.accountid].user - .filter(_ => _.id !== action.payload.id) + ...state.entities[action.payload.accountid].user.filter(_ => _.id !== action.payload.id), ]; - return adapter.updateOne( - { id: action.payload.accountid, changes: { user: users } }, - state - ); + return adapter.updateOne({ id: action.payload.accountid, changes: { user: users } }, state); } default: { @@ -179,72 +164,43 @@ export function reducer( } } - export const getAccountsState = createFeatureSelector('accounts'); -export const getAccountsEntitiesState = createSelector( - getAccountsState, - state => state.list -); +export const getAccountsEntitiesState = createSelector(getAccountsState, state => state.list); -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getAccountsEntitiesState); - -export const isLoading = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getAccountsEntitiesState, - state => state.loading ); +export const isLoading = createSelector(getAccountsEntitiesState, state => state.loading); + export const getSelectedId = createSelector( getAccountsEntitiesState, - state => state.selectedAccountId + state => state.selectedAccountId, ); export const getSelectedAccount = createSelector( getAccountsState, getSelectedId, - (state, selectedId) => state.list.entities[selectedId] + (state, selectedId) => state.list.entities[selectedId], ); -export const filters = createSelector( - getAccountsEntitiesState, - state => state.filters -); +export const filters = createSelector(getAccountsEntitiesState, state => state.filters); +export const filterSelectedRoleTypes = createSelector(filters, state => state.selectedRoleTypes); -export const filterSelectedRoleTypes = createSelector( - filters, - state => state.selectedRoleTypes -); +export const filterSelectedDomainIds = createSelector(filters, state => state.selectedDomainIds); -export const filterSelectedDomainIds = createSelector( - filters, - state => state.selectedDomainIds -); +export const filterSelectedRoleNames = createSelector(filters, state => state.selectedRoleNames); -export const filterSelectedRoleNames = createSelector( - filters, - state => state.selectedRoleNames -); - -export const filterSelectedStates = createSelector( - filters, - state => state.selectedStates -); +export const filterSelectedStates = createSelector(filters, state => state.selectedStates); -export const filterSelectedGroupings = createSelector( - filters, - state => state.selectedGroupings -); +export const filterSelectedGroupings = createSelector(filters, state => state.selectedGroupings); export const selectUserAccount = createSelector( selectEntities, fromAuth.getUserAccountId, - (accountsMap, accountId) => accountsMap[accountId] + (accountsMap, accountId) => accountsMap[accountId], ); export const selectFilteredAccounts = createSelector( @@ -259,39 +215,36 @@ export const selectFilteredAccounts = createSelector( const domainIdsMap = selectedDomainIds.reduce((m, i) => ({ ...m, [i]: i }), {}); const statesMap = selectedStates.reduce((m, i) => ({ ...m, [i]: i }), {}); + const selectedRoleTypesFilter = account => + !selectedRoleTypes.length || !!roleTypeMap[account.roletype]; - const selectedRoleTypesFilter = - account => !selectedRoleTypes.length || !!roleTypeMap[account.roletype]; - - const selectedRoleNamesFilter = - account => !selectedRoleNames.length || !!roleNamesMap[account.rolename]; + const selectedRoleNamesFilter = account => + !selectedRoleNames.length || !!roleNamesMap[account.rolename]; - const selectedDomainIdsFilter = - account => !selectedDomainIds.length || !!domainIdsMap[account.domainid]; - - const selectedStatesFilter = - account => !selectedStates.length || !!statesMap[account.state]; + const selectedDomainIdsFilter = account => + !selectedDomainIds.length || !!domainIdsMap[account.domainid]; + const selectedStatesFilter = account => !selectedStates.length || !!statesMap[account.state]; return accounts.filter(account => { - return selectedDomainIdsFilter(account) - && selectedStatesFilter(account) - && selectedRoleTypesFilter(account) - && selectedRoleNamesFilter(account); + return ( + selectedDomainIdsFilter(account) && + selectedStatesFilter(account) && + selectedRoleTypesFilter(account) && + selectedRoleNamesFilter(account) + ); }); - } + }, ); export const selectDomainAccounts = createSelector( selectAll, fromAuth.getUserAccount, (accounts, userAccount) => { - const userDomainFilter = - account => !!userAccount && account.domainid === userAccount.domainid; + const userDomainFilter = account => !!userAccount && account.domainid === userAccount.domainid; return accounts.filter(account => { return userDomainFilter(account); }); - } + }, ); - diff --git a/src/app/reducers/affinity-groups/redux/affinity-groups.actions.ts b/src/app/reducers/affinity-groups/redux/affinity-groups.actions.ts index 114bb950fd..a68b0e5218 100644 --- a/src/app/reducers/affinity-groups/redux/affinity-groups.actions.ts +++ b/src/app/reducers/affinity-groups/redux/affinity-groups.actions.ts @@ -7,15 +7,13 @@ export const LOAD_AFFINITY_GROUPS_RESPONSE = '[AFFINITY GROUPS] LOAD_AFFINITY_GR export class LoadAffinityGroupsRequest implements Action { type = LOAD_AFFINITY_GROUPS_REQUEST; - constructor(public payload?: any) { - } + constructor(public payload?: any) {} } export class LoadAffinityGroupsResponse implements Action { type = LOAD_AFFINITY_GROUPS_RESPONSE; - constructor(public payload: Array) { - } + constructor(public payload: AffinityGroup[]) {} } export type Actions = LoadAffinityGroupsRequest | LoadAffinityGroupsResponse; diff --git a/src/app/reducers/affinity-groups/redux/affinity-groups.effects.ts b/src/app/reducers/affinity-groups/redux/affinity-groups.effects.ts index fe4754866c..62d52b29f0 100644 --- a/src/app/reducers/affinity-groups/redux/affinity-groups.effects.ts +++ b/src/app/reducers/affinity-groups/redux/affinity-groups.effects.ts @@ -11,19 +11,19 @@ import * as affinityGroupActions from './affinity-groups.actions'; @Injectable() export class AffinityGroupsEffects { - @Effect() loadAffinityGroups$: Observable = this.actions$.pipe( ofType(affinityGroupActions.LOAD_AFFINITY_GROUPS_REQUEST), switchMap((action: affinityGroupActions.LoadAffinityGroupsRequest) => { return this.affinityGroupService.getList().pipe( - map((affinityGroups: AffinityGroup[]) => new affinityGroupActions.LoadAffinityGroupsResponse(affinityGroups)), - catchError(() => of(new affinityGroupActions.LoadAffinityGroupsResponse([])))); - })); + map( + (affinityGroups: AffinityGroup[]) => + new affinityGroupActions.LoadAffinityGroupsResponse(affinityGroups), + ), + catchError(() => of(new affinityGroupActions.LoadAffinityGroupsResponse([]))), + ); + }), + ); - constructor( - private actions$: Actions, - private affinityGroupService: AffinityGroupService - ) { - } + constructor(private actions$: Actions, private affinityGroupService: AffinityGroupService) {} } diff --git a/src/app/reducers/affinity-groups/redux/affinity-groups.reducers.ts b/src/app/reducers/affinity-groups/redux/affinity-groups.reducers.ts index 53b15981dd..9958fa7af1 100644 --- a/src/app/reducers/affinity-groups/redux/affinity-groups.reducers.ts +++ b/src/app/reducers/affinity-groups/redux/affinity-groups.reducers.ts @@ -33,7 +33,7 @@ export const affinityGroupReducers = { */ export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: AffinityGroup) => item.id, - sortComparer: false + sortComparer: false, }); /** getInitialState returns the default initial state @@ -44,15 +44,12 @@ export const initialState: State = adapter.getInitialState({ loading: false, }); -export function reducer( - state = initialState, - action: affinityGroupActions.Actions -): State { +export function reducer(state = initialState, action: affinityGroupActions.Actions): State { switch (action.type) { case affinityGroupActions.LOAD_AFFINITY_GROUPS_REQUEST: { return { ...state, - loading: true + loading: true, }; } case affinityGroupActions.LOAD_AFFINITY_GROUPS_RESPONSE: { @@ -65,7 +62,7 @@ export function reducer( * sort each record upon entry into the sorted array. */ ...adapter.addAll(action.payload, state), - loading: false + loading: false, }; } default: { @@ -74,22 +71,15 @@ export function reducer( } } - export const getAffinityGroupsState = createFeatureSelector('affinity-groups'); export const getAffinityGroupEntitiesState = createSelector( getAffinityGroupsState, - state => state.list + state => state.list, ); -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getAffinityGroupEntitiesState); - -export const isLoading = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getAffinityGroupEntitiesState, - state => state.loading ); + +export const isLoading = createSelector(getAffinityGroupEntitiesState, state => state.loading); diff --git a/src/app/reducers/auth/redux/auth.actions.ts b/src/app/reducers/auth/redux/auth.actions.ts index 83056f79bf..3916ce7600 100644 --- a/src/app/reducers/auth/redux/auth.actions.ts +++ b/src/app/reducers/auth/redux/auth.actions.ts @@ -3,22 +3,16 @@ import { Action } from '@ngrx/store'; export const LOAD_USER_ACCOUNT_REQUEST = '[USER ACCOUNT] LOAD_USER_ACCOUNT_REQUEST'; export const LOAD_USER_ACCOUNT_RESPONSE = '[USER ACCOUNT] LOAD_USER_ACCOUNT_RESPONSE'; - export class LoadUserAccountRequest implements Action { type = LOAD_USER_ACCOUNT_REQUEST; - constructor(public payload?: any) { - } - + constructor(public payload?: any) {} } export class LoadUserAccountResponse implements Action { type = LOAD_USER_ACCOUNT_RESPONSE; - constructor(public payload: any ) { - } - + constructor(public payload: any) {} } -export type Actions = LoadUserAccountRequest - | LoadUserAccountResponse; +export type Actions = LoadUserAccountRequest | LoadUserAccountResponse; diff --git a/src/app/reducers/auth/redux/auth.effects.ts b/src/app/reducers/auth/redux/auth.effects.ts index 869baf430c..c50f761378 100644 --- a/src/app/reducers/auth/redux/auth.effects.ts +++ b/src/app/reducers/auth/redux/auth.effects.ts @@ -10,7 +10,6 @@ import { AccountService } from '../../../shared/services/account.service'; @Injectable() export class UserAccountEffects { - @Effect() loadUserAccount$: Observable = this.actions$.pipe( ofType(authActions.LOAD_USER_ACCOUNT_REQUEST), @@ -19,12 +18,10 @@ export class UserAccountEffects { map((account: Account) => { return new authActions.LoadUserAccountResponse(account); }), - catchError(() => of(new authActions.LoadUserAccountResponse({})))); - })); + catchError(() => of(new authActions.LoadUserAccountResponse({}))), + ); + }), + ); - constructor( - private actions$: Actions, - private accountService: AccountService - ) { - } + constructor(private actions$: Actions, private accountService: AccountService) {} } diff --git a/src/app/reducers/auth/redux/auth.reducers.ts b/src/app/reducers/auth/redux/auth.reducers.ts index df5b7cf043..1496e0ee99 100644 --- a/src/app/reducers/auth/redux/auth.reducers.ts +++ b/src/app/reducers/auth/redux/auth.reducers.ts @@ -1,7 +1,4 @@ -import { - createFeatureSelector, - createSelector -} from '@ngrx/store'; +import { createFeatureSelector, createSelector } from '@ngrx/store'; import * as event from './auth.actions'; import { Account } from '../../../shared/models/account.model'; @@ -14,9 +11,9 @@ import { Account } from '../../../shared/models/account.model'; */ export interface State { - loading: boolean, + loading: boolean; account: Account; - accountId: string + accountId: string; } export interface UserAccountState { @@ -26,7 +23,7 @@ export interface UserAccountState { const initialUserAccountState: State = { loading: false, account: null, - accountId: '' + accountId: '', }; export const userAccountReducers = { @@ -38,7 +35,7 @@ export function reducer(state = initialUserAccountState, action: event.Actions): case event.LOAD_USER_ACCOUNT_REQUEST: { return { ...state, - loading: true + loading: true, }; } case event.LOAD_USER_ACCOUNT_RESPONSE: { @@ -46,7 +43,7 @@ export function reducer(state = initialUserAccountState, action: event.Actions): ...state, account: action.payload, accountId: action.payload.id, - loading: false + loading: false, }; } default: { @@ -57,23 +54,10 @@ export function reducer(state = initialUserAccountState, action: event.Actions): export const getUserAccountState = createFeatureSelector('userAccount'); +export const getUserAccountEntity = createSelector(getUserAccountState, state => state.entity); -export const getUserAccountEntity = createSelector( - getUserAccountState, - state => state.entity -); +export const getUserAccount = createSelector(getUserAccountEntity, state => state.account); -export const getUserAccount = createSelector( - getUserAccountEntity, - state => state.account -); +export const getUserAccountId = createSelector(getUserAccountEntity, state => state.accountId); -export const getUserAccountId = createSelector( - getUserAccountEntity, - state => state.accountId -); - -export const isLoading = createSelector( - getUserAccountEntity, - state => state.loading -); +export const isLoading = createSelector(getUserAccountEntity, state => state.loading); diff --git a/src/app/reducers/configuration/redux/configurations.actions.ts b/src/app/reducers/configuration/redux/configurations.actions.ts index c63ba0559a..409a9498d7 100644 --- a/src/app/reducers/configuration/redux/configurations.actions.ts +++ b/src/app/reducers/configuration/redux/configurations.actions.ts @@ -8,37 +8,29 @@ export const UPDATE_CONFIGURATIONS_ERROR = '[CONFIGURATIONS] UPDATE_CONFIGURATIO export class LoadConfigurationsRequest implements Action { type = LOAD_CONFIGURATIONS_REQUEST; - constructor(public payload?: any) { - } - + constructor(public payload?: any) {} } export class LoadConfigurationsResponse implements Action { type = LOAD_CONFIGURATIONS_RESPONSE; - constructor(public payload: any ) { - } - + constructor(public payload: any) {} } export class UpdateConfigurationRequest implements Action { type = UPDATE_CONFIGURATIONS_REQUEST; - constructor(public payload: any ) { - } - + constructor(public payload: any) {} } export class UpdateConfigurationError implements Action { readonly type = UPDATE_CONFIGURATIONS_ERROR; - constructor(public payload: Error) { - } - + constructor(public payload: Error) {} } - -export type Actions = LoadConfigurationsRequest +export type Actions = + | LoadConfigurationsRequest | LoadConfigurationsResponse | UpdateConfigurationRequest | UpdateConfigurationError; diff --git a/src/app/reducers/configuration/redux/configurations.effects.ts b/src/app/reducers/configuration/redux/configurations.effects.ts index 8220799ce2..fc5e30e5be 100644 --- a/src/app/reducers/configuration/redux/configurations.effects.ts +++ b/src/app/reducers/configuration/redux/configurations.effects.ts @@ -15,7 +15,6 @@ import * as configurationActions from './configurations.actions'; @Injectable() export class ConfigurationEffects { - @Effect() loadConfigurations$: Observable = this.actions$.pipe( ofType(configurationActions.LOAD_CONFIGURATIONS_REQUEST), @@ -23,50 +22,53 @@ export class ConfigurationEffects { switchMap(([action, account]: [configurationActions.LoadConfigurationsRequest, Account]) => { return account && isRootAdmin(account) ? this.configurationService.getList(action.payload).pipe( - map((configurations: Configuration[]) => { - return new configurationActions.LoadConfigurationsResponse(configurations); - }), - catchError(() => of(new configurationActions.LoadConfigurationsResponse([])))) + map((configurations: Configuration[]) => { + return new configurationActions.LoadConfigurationsResponse(configurations); + }), + catchError(() => of(new configurationActions.LoadConfigurationsResponse([]))), + ) : of(new configurationActions.LoadConfigurationsResponse([])); - })); + }), + ); @Effect() updateConfiguration$: Observable = this.actions$.pipe( ofType(configurationActions.UPDATE_CONFIGURATIONS_REQUEST), mergeMap((action: configurationActions.UpdateConfigurationRequest) => { - return this.configurationService.updateConfiguration( - action.payload.configuration, - action.payload.account).pipe( - map(() => { - return new configurationActions.LoadConfigurationsRequest({ - accountid: action.payload.account.id - }); - }), - catchError((error) => of(new configurationActions.UpdateConfigurationError(error)))); - })); + return this.configurationService + .updateConfiguration(action.payload.configuration, action.payload.account) + .pipe( + map(() => { + return new configurationActions.LoadConfigurationsRequest({ + accountid: action.payload.account.id, + }); + }), + catchError(error => of(new configurationActions.UpdateConfigurationError(error))), + ); + }), + ); @Effect({ dispatch: false }) updateConfigurationError$: Observable = this.actions$.pipe( ofType(configurationActions.UPDATE_CONFIGURATIONS_ERROR), tap((action: configurationActions.UpdateConfigurationError) => { this.handleError(action.payload); - })); - + }), + ); constructor( private store: Store, private actions$: Actions, private configurationService: ConfigurationService, - private dialogService: DialogService - ) { - } + private dialogService: DialogService, + ) {} private handleError(error): void { this.dialogService.alert({ message: { translationToken: error.message, - interpolateParams: error.params - } + interpolateParams: error.params, + }, }); } } diff --git a/src/app/reducers/configuration/redux/configurations.reducers.ts b/src/app/reducers/configuration/redux/configurations.reducers.ts index 486a97efb4..f75e0235a4 100644 --- a/src/app/reducers/configuration/redux/configurations.reducers.ts +++ b/src/app/reducers/configuration/redux/configurations.reducers.ts @@ -1,12 +1,5 @@ -import { - createFeatureSelector, - createSelector -} from '@ngrx/store'; -import { - createEntityAdapter, - EntityAdapter, - EntityState -} from '@ngrx/entity'; +import { createFeatureSelector, createSelector } from '@ngrx/store'; +import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; import * as event from './configurations.actions'; import { Configuration } from '../../../shared/models/configuration.model'; @@ -18,7 +11,7 @@ import { Configuration } from '../../../shared/models/configuration.model'; * any additional interface properties. */ export interface State extends EntityState { - loading: boolean + loading: boolean; } export interface ConfigurationsState { @@ -39,7 +32,7 @@ export const configurationReducers = { */ export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: Configuration) => item.name, - sortComparer: false + sortComparer: false, }); /** getInitialState returns the default initial state @@ -47,23 +40,19 @@ export const adapter: EntityAdapter = createEntityAdapter('configurations'); export const getConfigurationsEntitiesState = createSelector( getConfigurationsState, - state => state.list + state => state.list, ); -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getConfigurationsEntitiesState); - -export const isLoading = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getConfigurationsEntitiesState, - state => state.loading ); - +export const isLoading = createSelector(getConfigurationsEntitiesState, state => state.loading); diff --git a/src/app/reducers/disk-offerings/redux/disk-offerings.actions.ts b/src/app/reducers/disk-offerings/redux/disk-offerings.actions.ts index c1b6e1e564..d120fa7d8a 100644 --- a/src/app/reducers/disk-offerings/redux/disk-offerings.actions.ts +++ b/src/app/reducers/disk-offerings/redux/disk-offerings.actions.ts @@ -8,25 +8,19 @@ export const LOAD_DEFAULT_DISK_PARAMS_RESPONSE = '[OFFERINGS] LOAD_DEFAULT_DISK_ export class LoadOfferingsRequest implements Action { type = LOAD_DISK_OFFERINGS_REQUEST; - constructor(public payload?: any) { - } - + constructor(public payload?: any) {} } export class LoadOfferingsResponse implements Action { type = LOAD_DISK_OFFERINGS_RESPONSE; - constructor(public payload: any ) { - } - + constructor(public payload: any) {} } export class LoadDefaultParamsResponse implements Action { type = LOAD_DEFAULT_DISK_PARAMS_RESPONSE; - constructor(public payload: Array ) { - } + constructor(public payload: string[]) {} } -export type Actions = LoadOfferingsResponse - | LoadOfferingsRequest; +export type Actions = LoadOfferingsResponse | LoadOfferingsRequest; diff --git a/src/app/reducers/disk-offerings/redux/disk-offerings.effects.spec.ts b/src/app/reducers/disk-offerings/redux/disk-offerings.effects.spec.ts index 22017fdf6d..77d3f66e4b 100644 --- a/src/app/reducers/disk-offerings/redux/disk-offerings.effects.spec.ts +++ b/src/app/reducers/disk-offerings/redux/disk-offerings.effects.spec.ts @@ -11,7 +11,7 @@ import { TestStore } from '../../../../testutils/ngrx-test-store'; import * as actions from './disk-offerings.actions'; -const diskOfferings: Array = [ +const diskOfferings: DiskOffering[] = [ { displaytext: 'test snapshot', id: 'test-id', @@ -25,8 +25,8 @@ const diskOfferings: Array = [ miniops: 1, maxiops: 1, storagetype: 'any', - provisioningtype: 'any' - } + provisioningtype: 'any', + }, ]; export class TestActions extends Actions { @@ -35,6 +35,8 @@ export class TestActions extends Actions { } public set stream(source: Observable) { + // todo + // tslint:disable-next-line this.source = source; } } @@ -56,8 +58,8 @@ describe('Disk Offering Effects', () => { DiskOfferingService, DiskOfferingEffects, { provide: Actions, useFactory: getActions }, - { provide: Store, useClass: TestStore } - ] + { provide: Store, useClass: TestStore }, + ], }); actions$ = TestBed.get(Actions); service = TestBed.get(DiskOfferingService); diff --git a/src/app/reducers/disk-offerings/redux/disk-offerings.effects.ts b/src/app/reducers/disk-offerings/redux/disk-offerings.effects.ts index 8535a8b8a0..3fa9536c7e 100644 --- a/src/app/reducers/disk-offerings/redux/disk-offerings.effects.ts +++ b/src/app/reducers/disk-offerings/redux/disk-offerings.effects.ts @@ -10,7 +10,6 @@ import { DiskOffering } from '../../../shared/models'; @Injectable() export class DiskOfferingEffects { - @Effect() loadOfferings$: Observable = this.actions$.pipe( ofType(diskOfferingActions.LOAD_DISK_OFFERINGS_REQUEST), @@ -19,12 +18,10 @@ export class DiskOfferingEffects { map((offerings: DiskOffering[]) => { return new diskOfferingActions.LoadOfferingsResponse(offerings); }), - catchError(() => of(new diskOfferingActions.LoadOfferingsResponse([])))); - })); + catchError(() => of(new diskOfferingActions.LoadOfferingsResponse([]))), + ); + }), + ); - constructor( - private actions$: Actions, - private offeringService: DiskOfferingService - ) { - } + constructor(private actions$: Actions, private offeringService: DiskOfferingService) {} } diff --git a/src/app/reducers/disk-offerings/redux/disk-offerings.reducers.ts b/src/app/reducers/disk-offerings/redux/disk-offerings.reducers.ts index a3a14a83af..ac7e2882d9 100644 --- a/src/app/reducers/disk-offerings/redux/disk-offerings.reducers.ts +++ b/src/app/reducers/disk-offerings/redux/disk-offerings.reducers.ts @@ -8,7 +8,6 @@ import * as fromVolumes from '../../volumes/redux/volumes.reducers'; import * as fromZones from '../../zones/redux/zones.reducers'; import * as event from './disk-offerings.actions'; - export interface State extends EntityState { loading: boolean; } @@ -23,31 +22,27 @@ export const diskOfferingReducers = { export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: DiskOffering) => item.id, - sortComparer: false + sortComparer: false, }); export const initialState: State = adapter.getInitialState({ - loading: false + loading: false, }); -export function reducer( - state = initialState, - action: event.Actions -): State { +export function reducer(state = initialState, action: event.Actions): State { switch (action.type) { case event.LOAD_DISK_OFFERINGS_REQUEST: { return { ...state, - loading: true + loading: true, }; } case event.LOAD_DISK_OFFERINGS_RESPONSE: { - const offerings = action.payload; return { ...adapter.addAll(offerings, state), - loading: false + loading: false, }; } @@ -57,33 +52,24 @@ export function reducer( } } - export const getOfferingsState = createFeatureSelector('disk-offerings'); -export const getOfferingsEntitiesState = createSelector( - getOfferingsState, - state => state.list -); +export const getOfferingsEntitiesState = createSelector(getOfferingsState, state => state.list); -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getOfferingsEntitiesState); - -export const isLoading = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getOfferingsEntitiesState, - state => state.loading ); +export const isLoading = createSelector(getOfferingsEntitiesState, state => state.loading); + const isDiskOfferingAvailableInZone = ( offering: DiskOffering, offeringAvailability: ServiceOfferingAvailability, - zone: Zone + zone: Zone, ) => { if (offeringAvailability.zones[zone.id]) { - const isOfferingExist = offeringAvailability.zones[zone.id].diskOfferings.indexOf(offering.id) !== -1; + const isOfferingExist = + offeringAvailability.zones[zone.id].diskOfferings.indexOf(offering.id) !== -1; return isOfferingExist; } return false; @@ -92,29 +78,27 @@ const isDiskOfferingAvailableInZone = ( export const getSelectedOffering = createSelector( selectEntities, fromVolumes.getSelectedVolume, - (entities, volume) => volume && entities[volume.diskofferingid] + (entities, volume) => volume && entities[volume.diskofferingid], ); const getOfferingsAvailableInZone = ( - offeringList: Array, + offeringList: DiskOffering[], offeringAvailability: ServiceOfferingAvailability, - zone: Zone + zone: Zone, ) => { if (!offeringAvailability.filterOfferings) { return offeringList; } - return offeringList - .filter(offering => { - const offeringAvailableInZone = isDiskOfferingAvailableInZone( - offering, - offeringAvailability, - zone - ); - const localStorageCompatibility = zone.localstorageenabled || !isOfferingLocal( - offering); - return offeringAvailableInZone && localStorageCompatibility; - }); + return offeringList.filter(offering => { + const offeringAvailableInZone = isDiskOfferingAvailableInZone( + offering, + offeringAvailability, + zone, + ); + const localStorageCompatibility = zone.localstorageenabled || !isOfferingLocal(offering); + return offeringAvailableInZone && localStorageCompatibility; + }); }; export const getAvailableOfferings = createSelector( @@ -123,14 +107,9 @@ export const getAvailableOfferings = createSelector( fromZones.getSelectedZone, (diskOfferings, availability, zone) => { if (zone && availability) { - const availableOfferings = getOfferingsAvailableInZone( - diskOfferings, - availability, - zone - ); + const availableOfferings = getOfferingsAvailableInZone(diskOfferings, availability, zone); return availableOfferings; - } else { - return []; } - } + return []; + }, ); diff --git a/src/app/reducers/domains/redux/domains.actions.ts b/src/app/reducers/domains/redux/domains.actions.ts index fd1aac0d08..0c7ab65168 100644 --- a/src/app/reducers/domains/redux/domains.actions.ts +++ b/src/app/reducers/domains/redux/domains.actions.ts @@ -6,17 +6,13 @@ export const LOAD_DOMAINS_RESPONSE = '[DOMAINS] LOAD_DOMAINS_RESPONSE'; export class LoadDomainsRequest implements Action { type = LOAD_DOMAINS_REQUEST; - constructor(public payload?: any) { - } - + constructor(public payload?: any) {} } export class LoadDomainsResponse implements Action { type = LOAD_DOMAINS_RESPONSE; - constructor(public payload: any ) { - } - + constructor(public payload: any) {} } export type Actions = LoadDomainsResponse | LoadDomainsRequest; diff --git a/src/app/reducers/domains/redux/domains.effects.ts b/src/app/reducers/domains/redux/domains.effects.ts index b21aa2bd42..8dc59e027e 100644 --- a/src/app/reducers/domains/redux/domains.effects.ts +++ b/src/app/reducers/domains/redux/domains.effects.ts @@ -18,12 +18,10 @@ export class DomainsEffects { map((domains: Domain[]) => { return new domainActions.LoadDomainsResponse(domains); }), - catchError(() => of(new domainActions.LoadDomainsResponse([])))); - })); + catchError(() => of(new domainActions.LoadDomainsResponse([]))), + ); + }), + ); - constructor( - private actions$: Actions, - private domainService: DomainService - ) { - } + constructor(private actions$: Actions, private domainService: DomainService) {} } diff --git a/src/app/reducers/domains/redux/domains.reducers.ts b/src/app/reducers/domains/redux/domains.reducers.ts index 4d4a248690..42809584ac 100644 --- a/src/app/reducers/domains/redux/domains.reducers.ts +++ b/src/app/reducers/domains/redux/domains.reducers.ts @@ -1,12 +1,5 @@ -import { - createFeatureSelector, - createSelector -} from '@ngrx/store'; -import { - createEntityAdapter, - EntityAdapter, - EntityState -} from '@ngrx/entity'; +import { createFeatureSelector, createSelector } from '@ngrx/store'; +import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; import { Domain } from '../../../shared/models/domain.model'; import * as event from './domains.actions'; @@ -19,7 +12,7 @@ import * as event from './domains.actions'; * any additional interface properties. */ export interface State extends EntityState { - loading: boolean + loading: boolean; } export interface DomainsState { @@ -40,7 +33,7 @@ export const domainReducers = { */ export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: Domain) => item.id, - sortComparer: false + sortComparer: false, }); /** getInitialState returns the default initial state @@ -48,22 +41,18 @@ export const adapter: EntityAdapter = createEntityAdapter({ * additional properties can also be defined. */ export const initialState: State = adapter.getInitialState({ - loading: false + loading: false, }); -export function reducer( - state = initialState, - action: event.Actions -): State { +export function reducer(state = initialState, action: event.Actions): State { switch (action.type) { case event.LOAD_DOMAINS_REQUEST: { return { ...state, - loading: true + loading: true, }; } case event.LOAD_DOMAINS_RESPONSE: { - const domains = action.payload; return { @@ -75,7 +64,7 @@ export function reducer( * sort each record upon entry into the sorted array. */ ...adapter.addAll(domains, state), - loading: false + loading: false, }; } default: { @@ -84,22 +73,12 @@ export function reducer( } } - export const getDomainsState = createFeatureSelector('domains'); -export const getDomainsEntitiesState = createSelector( - getDomainsState, - state => state.list -); +export const getDomainsEntitiesState = createSelector(getDomainsState, state => state.list); -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getDomainsEntitiesState); - -export const isLoading = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getDomainsEntitiesState, - state => state.loading ); + +export const isLoading = createSelector(getDomainsEntitiesState, state => state.loading); diff --git a/src/app/reducers/index.ts b/src/app/reducers/index.ts index 2b72769a47..4d20fe202f 100644 --- a/src/app/reducers/index.ts +++ b/src/app/reducers/index.ts @@ -1,12 +1,10 @@ -import { ActionReducer, ActionReducerMap, MetaReducer, } from '@ngrx/store'; -import { environment } from '../../environments/environment'; +import { ActionReducerMap } from '@ngrx/store'; import * as fromRouter from '@ngrx/router-store'; /** * storeFreeze prevents state from being mutated. When mutation occurs, an * exception will be thrown. This is useful during development mode to * ensure that none of the reducers accidentally mutates the state. */ -import { storeFreeze } from 'ngrx-store-freeze'; import { RouterStateUrl } from '../root-store/custom-router-state-serializer'; /** @@ -34,33 +32,3 @@ export const reducers: ActionReducerMap = { // layout: fromLayout.reducer, routerReducer: fromRouter.routerReducer, }; - -// console.log all actions -export function logger(reducer: ActionReducer): ActionReducer { - return function(state: State, action: any): State { - console.log('state', state); - console.log('action', action); - - return reducer(state, action); - }; -} - -// clear store if user logs out -export function logout(reducer: ActionReducer) { - return function(state: State, action: any): State { - if (action.type === '[USER ACCOUNT] LOG_OUT_USER_ACCOUNT') { - state = undefined; - } - - return reducer(state, action); - } -} - -/** - * By default, @ngrx/store uses combineReducers with the reducer map to compose - * the root meta-reducer. To add more meta-reducers, provide an array of meta-reducers - * that will be composed to form the root meta-reducer. - */ -export const metaReducers: MetaReducer[] = !environment.production - ? [logger, logout, storeFreeze] - : [logout]; diff --git a/src/app/reducers/resource-count/redux/resource-counts.actions.ts b/src/app/reducers/resource-count/redux/resource-counts.actions.ts index 16ac5890ca..a754a0bc35 100644 --- a/src/app/reducers/resource-count/redux/resource-counts.actions.ts +++ b/src/app/reducers/resource-count/redux/resource-counts.actions.ts @@ -6,17 +6,13 @@ export const LOAD_RESOURCE_COUNTS_RESPONSE = '[RESOURCE_COUNTS] LOAD_RESOURCE_CO export class LoadResourceCountsRequest implements Action { type = LOAD_RESOURCE_COUNTS_REQUEST; - constructor(public payload?: any) { - } - + constructor(public payload?: any) {} } export class LoadResourceCountsResponse implements Action { type = LOAD_RESOURCE_COUNTS_RESPONSE; - constructor(public payload: any ) { - } - + constructor(public payload: any) {} } export type Actions = LoadResourceCountsResponse | LoadResourceCountsRequest; diff --git a/src/app/reducers/resource-count/redux/resource-counts.effects.ts b/src/app/reducers/resource-count/redux/resource-counts.effects.ts index b422a018d2..5ac168a701 100644 --- a/src/app/reducers/resource-count/redux/resource-counts.effects.ts +++ b/src/app/reducers/resource-count/redux/resource-counts.effects.ts @@ -10,7 +10,6 @@ import { ResourceCount } from '../../../shared/models/resource-count.model'; @Injectable() export class ResourceCountsEffects { - @Effect() loadResourceLimits$: Observable = this.actions$.pipe( ofType(resourceCountActions.LOAD_RESOURCE_COUNTS_REQUEST), @@ -19,12 +18,10 @@ export class ResourceCountsEffects { map((stats: ResourceCount[]) => { return new resourceCountActions.LoadResourceCountsResponse(stats); }), - catchError(() => of(new resourceCountActions.LoadResourceCountsResponse([])))); - })); + catchError(() => of(new resourceCountActions.LoadResourceCountsResponse([]))), + ); + }), + ); - constructor( - private actions$: Actions, - private resourceCountService: ResourceCountService - ) { - } + constructor(private actions$: Actions, private resourceCountService: ResourceCountService) {} } diff --git a/src/app/reducers/resource-count/redux/resource-counts.reducers.ts b/src/app/reducers/resource-count/redux/resource-counts.reducers.ts index e354108860..9f37199a99 100644 --- a/src/app/reducers/resource-count/redux/resource-counts.reducers.ts +++ b/src/app/reducers/resource-count/redux/resource-counts.reducers.ts @@ -1,12 +1,5 @@ -import { - createFeatureSelector, - createSelector -} from '@ngrx/store'; -import { - createEntityAdapter, - EntityAdapter, - EntityState -} from '@ngrx/entity'; +import { createFeatureSelector, createSelector } from '@ngrx/store'; +import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; import * as event from './resource-counts.actions'; import { ResourceCount } from '../../../shared/models/resource-count.model'; @@ -18,7 +11,7 @@ import { ResourceCount } from '../../../shared/models/resource-count.model'; * any additional interface properties. */ export interface State extends EntityState { - loading: boolean + loading: boolean; } export interface ResourceCountsState { @@ -39,7 +32,7 @@ export const resourceCountsReducers = { */ export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: ResourceCount) => item.resourcetype.toString(), - sortComparer: false + sortComparer: false, }); /** getInitialState returns the default initial state @@ -47,22 +40,18 @@ export const adapter: EntityAdapter = createEntityAdapter('resourceCounts'); export const getResourceCountsEntitiesState = createSelector( getResourceCountsState, - state => state.list + state => state.list, ); -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getResourceCountsEntitiesState); - -export const isLoading = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getResourceCountsEntitiesState, - state => state.loading ); - +export const isLoading = createSelector(getResourceCountsEntitiesState, state => state.loading); diff --git a/src/app/reducers/resource-limit/redux/resource-limits.actions.ts b/src/app/reducers/resource-limit/redux/resource-limits.actions.ts index 5030e841b3..77d373faa1 100644 --- a/src/app/reducers/resource-limit/redux/resource-limits.actions.ts +++ b/src/app/reducers/resource-limit/redux/resource-limits.actions.ts @@ -9,36 +9,29 @@ export const UPDATE_RESOURCE_LIMITS_ERROR = '[RESOURCE_LIMITS] UPDATE_RESOURCE_L export class LoadResourceLimitsRequest implements Action { type = LOAD_RESOURCE_LIMITS_REQUEST; - constructor(public payload: { account: string, domainid: string }) { - } - + constructor(public payload: { account: string; domainid: string }) {} } export class LoadResourceLimitsResponse implements Action { type = LOAD_RESOURCE_LIMITS_RESPONSE; - constructor(public payload: any ) { - } - + constructor(public payload: any) {} } export class UpdateResourceLimitsRequest implements Action { type = UPDATE_RESOURCE_LIMITS_REQUEST; - constructor(public payload: ResourceLimit[] ) { - } - + constructor(public payload: ResourceLimit[]) {} } export class UpdateResourceLimitsError implements Action { readonly type = UPDATE_RESOURCE_LIMITS_ERROR; - constructor(public payload: Error) { - } - + constructor(public payload: Error) {} } -export type Actions = LoadResourceLimitsResponse +export type Actions = + | LoadResourceLimitsResponse | LoadResourceLimitsRequest | UpdateResourceLimitsRequest | UpdateResourceLimitsError; diff --git a/src/app/reducers/resource-limit/redux/resource-limits.effects.ts b/src/app/reducers/resource-limit/redux/resource-limits.effects.ts index 216c05d231..7b7d06ed0e 100644 --- a/src/app/reducers/resource-limit/redux/resource-limits.effects.ts +++ b/src/app/reducers/resource-limit/redux/resource-limits.effects.ts @@ -11,7 +11,6 @@ import { DialogService } from '../../../dialog/dialog-service/dialog.service'; @Injectable() export class ResourceLimitsEffects { - @Effect() loadResourseLimits$: Observable = this.actions$.pipe( ofType(resourceLimitActions.LOAD_RESOURCE_LIMITS_REQUEST), @@ -20,8 +19,10 @@ export class ResourceLimitsEffects { map((limits: ResourceLimit[]) => { return new resourceLimitActions.LoadResourceLimitsResponse(limits); }), - catchError(() => of(new resourceLimitActions.LoadResourceLimitsResponse([])))); - })); + catchError(() => of(new resourceLimitActions.LoadResourceLimitsResponse([]))), + ); + }), + ); @Effect() updateResourceLimits$: Observable = this.actions$.pipe( @@ -31,38 +32,41 @@ export class ResourceLimitsEffects { const domainid = action.payload[0].domainid; const observes = action.payload.map(limit => - this.resourceLimitService.updateResourceLimit(limit)); + this.resourceLimitService.updateResourceLimit(limit), + ); return forkJoin(observes).pipe( map(() => { return new resourceLimitActions.LoadResourceLimitsRequest({ domainid, - account + account, }); }), - catchError((error) => of(new resourceLimitActions.UpdateResourceLimitsError(error)))); - })); + catchError(error => of(new resourceLimitActions.UpdateResourceLimitsError(error))), + ); + }), + ); @Effect({ dispatch: false }) updateResourceLimitsError$: Observable = this.actions$.pipe( ofType(resourceLimitActions.UPDATE_RESOURCE_LIMITS_ERROR), tap((action: resourceLimitActions.UpdateResourceLimitsError) => { this.handleError(action.payload); - })); + }), + ); constructor( private actions$: Actions, private resourceLimitService: ResourceLimitService, - private dialogService: DialogService - ) { - } + private dialogService: DialogService, + ) {} private handleError(error): void { this.dialogService.alert({ message: { translationToken: error.message, - interpolateParams: error.params - } + interpolateParams: error.params, + }, }); } } diff --git a/src/app/reducers/resource-limit/redux/resource-limits.reducers.ts b/src/app/reducers/resource-limit/redux/resource-limits.reducers.ts index 502811c5d1..bd9ff65e9f 100644 --- a/src/app/reducers/resource-limit/redux/resource-limits.reducers.ts +++ b/src/app/reducers/resource-limit/redux/resource-limits.reducers.ts @@ -12,7 +12,7 @@ import * as event from './resource-limits.actions'; * any additional interface properties. */ export interface State extends EntityState { - loading: boolean + loading: boolean; } export interface ResourceLimitsState { @@ -35,7 +35,7 @@ const sortByResourceTypes = (a: ResourceLimit, b: ResourceLimit) => a.resourcety */ export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: ResourceLimit) => item.resourcetype.toString(), - sortComparer: sortByResourceTypes + sortComparer: sortByResourceTypes, }); /** getInitialState returns the default initial state @@ -43,18 +43,15 @@ export const adapter: EntityAdapter = createEntityAdapter('resourceLimits'); export const getResourceLimitsEntitiesState = createSelector( getResourceLimitsState, - state => state.list + state => state.list, ); export const { @@ -93,7 +88,4 @@ export const { selectTotal, } = adapter.getSelectors(getResourceLimitsEntitiesState); -export const isLoading = createSelector( - getResourceLimitsEntitiesState, - state => state.loading -); +export const isLoading = createSelector(getResourceLimitsEntitiesState, state => state.loading); diff --git a/src/app/reducers/roles/redux/roles.actions.ts b/src/app/reducers/roles/redux/roles.actions.ts index a2c47285b7..ef488bf00d 100644 --- a/src/app/reducers/roles/redux/roles.actions.ts +++ b/src/app/reducers/roles/redux/roles.actions.ts @@ -6,17 +6,13 @@ export const LOAD_ROLES_RESPONSE = '[ROLES] LOAD_ROLES_RESPONSE'; export class LoadRolesRequest implements Action { type = LOAD_ROLES_REQUEST; - constructor(public payload?: any) { - } - + constructor(public payload?: any) {} } export class LoadRolesResponse implements Action { type = LOAD_ROLES_RESPONSE; - constructor(public payload: any ) { - } - + constructor(public payload: any) {} } export type Actions = LoadRolesResponse | LoadRolesRequest; diff --git a/src/app/reducers/roles/redux/roles.effects.ts b/src/app/reducers/roles/redux/roles.effects.ts index 3b150ef470..76d651a8b3 100644 --- a/src/app/reducers/roles/redux/roles.effects.ts +++ b/src/app/reducers/roles/redux/roles.effects.ts @@ -10,7 +10,6 @@ import { Role } from '../../../shared/models/role.model'; @Injectable() export class RolesEffects { - @Effect() loadRoles$: Observable = this.actions$.pipe( ofType(roleActions.LOAD_ROLES_REQUEST), @@ -19,12 +18,10 @@ export class RolesEffects { map((roles: Role[]) => { return new roleActions.LoadRolesResponse(roles); }), - catchError(() => of(new roleActions.LoadRolesResponse([])))); - })); + catchError(() => of(new roleActions.LoadRolesResponse([]))), + ); + }), + ); - constructor( - private actions$: Actions, - private roleService: RoleService - ) { - } + constructor(private actions$: Actions, private roleService: RoleService) {} } diff --git a/src/app/reducers/roles/redux/roles.reducers.ts b/src/app/reducers/roles/redux/roles.reducers.ts index d5321ddb5e..ef6f4dddd6 100644 --- a/src/app/reducers/roles/redux/roles.reducers.ts +++ b/src/app/reducers/roles/redux/roles.reducers.ts @@ -1,12 +1,5 @@ -import { - createFeatureSelector, - createSelector -} from '@ngrx/store'; -import { - createEntityAdapter, - EntityAdapter, - EntityState -} from '@ngrx/entity'; +import { createFeatureSelector, createSelector } from '@ngrx/store'; +import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; import * as event from './roles.actions'; import { Role } from '../../../shared/models/role.model'; @@ -18,7 +11,7 @@ import { Role } from '../../../shared/models/role.model'; * any additional interface properties. */ export interface State extends EntityState { - loading: boolean + loading: boolean; } export interface RolesState { @@ -39,7 +32,7 @@ export const roleReducers = { */ export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: Role) => item.id, - sortComparer: false + sortComparer: false, }); /** getInitialState returns the default initial state @@ -47,22 +40,18 @@ export const adapter: EntityAdapter = createEntityAdapter({ * additional properties can also be defined. */ export const initialState: State = adapter.getInitialState({ - loading: false + loading: false, }); -export function reducer( - state = initialState, - action: event.Actions -): State { +export function reducer(state = initialState, action: event.Actions): State { switch (action.type) { case event.LOAD_ROLES_REQUEST: { return { ...state, - loading: true + loading: true, }; } case event.LOAD_ROLES_RESPONSE: { - const roles = action.payload; return { @@ -74,40 +63,26 @@ export function reducer( * sort each record upon entry into the sorted array. */ ...adapter.addAll(roles, state), - loading: false + loading: false, }; } - default: { return state; } } } - export const getRolesState = createFeatureSelector('roles'); -export const getRolesEntitiesState = createSelector( - getRolesState, - state => state.list -); - -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getRolesEntitiesState); +export const getRolesEntitiesState = createSelector(getRolesState, state => state.list); -export const isLoading = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getRolesEntitiesState, - state => state.loading -); - -export const roleTypes = createSelector( - selectAll, - roles => Array.from(new Set(roles.map(role => role.type))) ); +export const isLoading = createSelector(getRolesEntitiesState, state => state.loading); +export const roleTypes = createSelector(selectAll, roles => + Array.from(new Set(roles.map(role => role.type))), +); diff --git a/src/app/reducers/security-groups/redux/sg.actions.ts b/src/app/reducers/security-groups/redux/sg.actions.ts index 9dde4dac0b..00b07dd107 100644 --- a/src/app/reducers/security-groups/redux/sg.actions.ts +++ b/src/app/reducers/security-groups/redux/sg.actions.ts @@ -17,133 +17,117 @@ export const DELETE_SECURITY_GROUP = '[SecurityGroups] DELETE_SECURITY_GROUP'; export const DELETE_PRIVATE_SECURITY_GROUP = '[SecurityGroups] DELETE_PRIVATE_SECURITY_GROUP'; export const DELETE_SECURITY_GROUP_SUCCESS = '[SecurityGroups] DELETE_SECURITY_GROUP_SUCCESS'; export const DELETE_SECURITY_GROUP_ERROR = '[SecurityGroups] DELETE_SECURITY_GROUP_ERROR'; -export const CONVERT_SECURITY_GROUP = '[Security Groups list] Convert private security group to shared'; -export const CONVERT_SECURITY_GROUP_SUCCESS = '[Security Groups list] Convert private security group to shared success'; -export const CONVERT_SECURITY_GROUP_ERROR = '[Security Groups list] Convert private security group to shared error'; +export const CONVERT_SECURITY_GROUP = + '[Security Groups list] Convert private security group to shared'; +export const CONVERT_SECURITY_GROUP_SUCCESS = + '[Security Groups list] Convert private security group to shared success'; +export const CONVERT_SECURITY_GROUP_ERROR = + '[Security Groups list] Convert private security group to shared error'; export class LoadSecurityGroupRequest implements Action { readonly type = LOAD_SECURITY_GROUP_REQUEST; - constructor(readonly payload?: any) { - } + constructor(readonly payload?: any) {} } export class LoadSecurityGroupResponse implements Action { readonly type = LOAD_SECURITY_GROUP_RESPONSE; - constructor(readonly payload: SecurityGroup[]) { - } + constructor(readonly payload: SecurityGroup[]) {} } export class SecurityGroupFilterUpdate implements Action { readonly type = SECURITY_GROUP_FILTER_UPDATE; - constructor(readonly payload?: any) { - } + constructor(readonly payload?: any) {} } export class LoadSelectedSecurityGroup implements Action { readonly type = LOAD_SELECTED_SECURITY_GROUP; - constructor(readonly payload: string) { - } + constructor(readonly payload: string) {} } export class CreateSecurityGroup implements Action { readonly type = CREATE_SECURITY_GROUP; - constructor(readonly payload: SecurityGroupCreationParams) { - } + constructor(readonly payload: SecurityGroupCreationParams) {} } export class CreateSecurityGroupSuccess implements Action { readonly type = CREATE_SECURITY_GROUP_SUCCESS; - constructor(readonly payload: SecurityGroup) { - } + constructor(readonly payload: SecurityGroup) {} } export class CreateSecurityGroupsSuccess implements Action { readonly type = CREATE_SECURITY_GROUPS_SUCCESS; - constructor(readonly payload: SecurityGroup[]) { - } + constructor(readonly payload: SecurityGroup[]) {} } export class CreateSecurityGroupError implements Action { readonly type = CREATE_SECURITY_GROUP_ERROR; - constructor(readonly payload: Error) { - } + constructor(readonly payload: Error) {} } export class UpdateSecurityGroup implements Action { readonly type = UPDATE_SECURITY_GROUP; - constructor(readonly payload: SecurityGroup) { - } + constructor(readonly payload: SecurityGroup) {} } export class DeleteSecurityGroup implements Action { readonly type = DELETE_SECURITY_GROUP; - constructor(readonly payload: SecurityGroup) { - } + constructor(readonly payload: SecurityGroup) {} } export class DeletePrivateSecurityGroup implements Action { readonly type = DELETE_PRIVATE_SECURITY_GROUP; - constructor(readonly payload: VirtualMachine) { - } + constructor(readonly payload: VirtualMachine) {} } export class DeleteSecurityGroupSuccess implements Action { readonly type = DELETE_SECURITY_GROUP_SUCCESS; - constructor(readonly payload: SecurityGroup) { - } + constructor(readonly payload: SecurityGroup) {} } export class DeleteSecurityGroupError implements Action { readonly type = DELETE_SECURITY_GROUP_ERROR; - constructor(readonly payload: Error) { - } + constructor(readonly payload: Error) {} } export class ConvertSecurityGroup implements Action { readonly type = CONVERT_SECURITY_GROUP; - constructor(readonly payload: SecurityGroupNative) { - } + constructor(readonly payload: SecurityGroupNative) {} } export class ConvertSecurityGroupSuccess implements Action { readonly type = CONVERT_SECURITY_GROUP_SUCCESS; - constructor(readonly payload: SecurityGroupNative) { - } + constructor(readonly payload: SecurityGroupNative) {} } export class ConvertSecurityGroupError implements Action { readonly type = CONVERT_SECURITY_GROUP_ERROR; - constructor(readonly payload: Error) { - } + constructor(readonly payload: Error) {} } export class UpdateSecurityGroupError implements Action { readonly type = UPDATE_SECURITY_GROUP_ERROR; - - constructor(readonly payload: Error) { - } + constructor(readonly payload: Error) {} } - export type Actions = - LoadSecurityGroupRequest + | LoadSecurityGroupRequest | LoadSecurityGroupResponse | SecurityGroupFilterUpdate | LoadSelectedSecurityGroup diff --git a/src/app/reducers/security-groups/redux/sg.effects.ts b/src/app/reducers/security-groups/redux/sg.effects.ts index d5300550ec..bf4faf0d27 100644 --- a/src/app/reducers/security-groups/redux/sg.effects.ts +++ b/src/app/reducers/security-groups/redux/sg.effects.ts @@ -4,7 +4,16 @@ import { MatDialog } from '@angular/material'; import { Action, select, Store } from '@ngrx/store'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { forkJoin, Observable, of } from 'rxjs'; -import { catchError, filter, first, map, mergeMap, switchMap, tap, withLatestFrom } from 'rxjs/operators'; +import { + catchError, + filter, + first, + map, + mergeMap, + switchMap, + tap, + withLatestFrom, +} from 'rxjs/operators'; import { SecurityGroupService } from '../../../security-group/services/security-group.service'; import { Rules } from '../../../shared/components/security-group-builder/rules'; @@ -27,12 +36,19 @@ export class SecurityGroupEffects { switchMap(() => { return forkJoin([ this.securityGroupService.getList(), - this.store.pipe(select(configSelectors.get('securityGroupTemplates')), first()) + this.store.pipe( + select(configSelectors.get('securityGroupTemplates')), + first(), + ), ]).pipe( - map(([groups, templates]) => new securityGroupActions - .LoadSecurityGroupResponse(groups.concat(templates))), - catchError(() => of(new securityGroupActions.LoadSecurityGroupResponse([])))); - })); + map( + ([groups, templates]) => + new securityGroupActions.LoadSecurityGroupResponse(groups.concat(templates)), + ), + catchError(() => of(new securityGroupActions.LoadSecurityGroupResponse([]))), + ); + }), + ); @Effect() createSecurityGroup$: Observable = this.actions$.pipe( @@ -46,14 +62,17 @@ export class SecurityGroupEffects { map(sg => new securityGroupActions.CreateSecurityGroupSuccess(sg)), catchError(error => { this.showNotificationsOnFail(error); - return of(new securityGroupActions.CreateSecurityGroupError(error)) - })); - })); + return of(new securityGroupActions.CreateSecurityGroupError(error)); + }), + ); + }), + ); @Effect({ dispatch: false }) createSecurityGroupSuccess$: Observable = this.actions$.pipe( ofType(securityGroupActions.CREATE_SECURITY_GROUP_SUCCESS), - tap(() => this.dialog.closeAll())); + tap(() => this.dialog.closeAll()), + ); @Effect() deleteSecurityGroup$: Observable = this.actions$.pipe( @@ -67,19 +86,22 @@ export class SecurityGroupEffects { map(() => new securityGroupActions.DeleteSecurityGroupSuccess(action.payload)), catchError(error => { this.showNotificationsOnFail(error); - return of(new securityGroupActions.DeleteSecurityGroupError(error)) - })); - })); + return of(new securityGroupActions.DeleteSecurityGroupError(error)); + }), + ); + }), + ); @Effect() deletePrivateSecurityGroup$: Observable = this.actions$.pipe( ofType(securityGroupActions.DELETE_PRIVATE_SECURITY_GROUP), withLatestFrom(this.store.pipe(select(fromSecurityGroups.selectAll))), - map(([action, groups]: [securityGroupActions.DeletePrivateSecurityGroup, Array]) => { - const vmGroup = groups.find((group: SecurityGroup) => - action.payload.securityGroup && - !!action.payload.securityGroup.find(sg => sg.id === group.id) && - getType(group) === SecurityGroupType.Private + map(([action, groups]: [securityGroupActions.DeletePrivateSecurityGroup, SecurityGroup[]]) => { + const vmGroup = groups.find( + (group: SecurityGroup) => + action.payload.securitygroup && + !!action.payload.securitygroup.find(sg => sg.id === group.id) && + getType(group) === SecurityGroupType.Private, ); return vmGroup; }), @@ -94,8 +116,10 @@ export class SecurityGroupEffects { catchError(error => { this.showNotificationsOnFail(error); return of(new securityGroupActions.DeleteSecurityGroupError(error)); - })); - })); + }), + ); + }), + ); @Effect({ dispatch: false }) deleteSecurityGroupSuccessNavigate$ = this.actions$.pipe( @@ -106,42 +130,42 @@ export class SecurityGroupEffects { }), tap(() => { this.router.navigate(['./security-group'], { - queryParamsHandling: 'preserve' + queryParamsHandling: 'preserve', }); - })); + }), + ); @Effect() convertSecurityGroup$: Observable = this.actions$.pipe( ofType(securityGroupActions.CONVERT_SECURITY_GROUP), mergeMap((action: securityGroupActions.ConvertSecurityGroup) => { - return this.dialogService.confirm({ message: 'DIALOG_MESSAGES.SECURITY_GROUPS.CONFIRM_CONVERT' }) + return this.dialogService + .confirm({ message: 'DIALOG_MESSAGES.SECURITY_GROUPS.CONFIRM_CONVERT' }) .pipe( - filter(res => Boolean(res)), + filter(Boolean), switchMap(() => { - return this.sgTagService.convertToShared(action.payload) - .pipe( - tap(() => { - const message = 'NOTIFICATIONS.FIREWALL.CONVERT_PRIVATE_TO_SHARED_DONE'; - this.showNotificationsOnFinish(message); - }), - map(response => { - return new securityGroupActions.ConvertSecurityGroupSuccess(response); - }), - catchError(error => { - this.showNotificationsOnFail(error); - return of(new securityGroupActions.ConvertSecurityGroupError(error)); - }) - ) - }) + return this.sgTagService.convertToShared(action.payload).pipe( + tap(() => { + const message = 'NOTIFICATIONS.FIREWALL.CONVERT_PRIVATE_TO_SHARED_DONE'; + this.showNotificationsOnFinish(message); + }), + map(response => { + return new securityGroupActions.ConvertSecurityGroupSuccess(response); + }), + catchError(error => { + this.showNotificationsOnFail(error); + return of(new securityGroupActions.ConvertSecurityGroupError(error)); + }), + ); + }), ); - }) + }), ); - private deleteSuccessMessage = { [SecurityGroupType.CustomTemplate]: 'NOTIFICATIONS.FIREWALL.TEMPLATE_DELETE_DONE', [SecurityGroupType.Shared]: 'NOTIFICATIONS.FIREWALL.SHARED_GROUP_DELETE_DONE', - [SecurityGroupType.Private]: 'NOTIFICATIONS.FIREWALL.PRIVATE_GROUP_DELETE_DONE' + [SecurityGroupType.Private]: 'NOTIFICATIONS.FIREWALL.PRIVATE_GROUP_DELETE_DONE', }; constructor( @@ -152,24 +176,26 @@ export class SecurityGroupEffects { private snackBarService: SnackBarService, private router: Router, private dialog: MatDialog, - private sgTagService: SecurityGroupTagService - ) { - } - - public createSecurityGroup({ mode, data, rules }: SecurityGroupCreationParams): Observable { + private sgTagService: SecurityGroupTagService, + ) {} + + public createSecurityGroup({ + mode, + data, + rules, + }: SecurityGroupCreationParams): Observable { return this.getSecurityGroupCreationRequest(mode, data, rules); } private getSecurityGroupCreationRequest( mode: string, data: any, - rules: Rules + rules: Rules, ): Observable { if (mode === SecurityGroupViewMode.Templates) { return this.securityGroupService.createTemplate(data, rules); - } else { - return this.securityGroupService.createShared(data, rules); } + return this.securityGroupService.createShared(data, rules); } private deleteSecurityGroup(securityGroup: SecurityGroup): Observable { @@ -182,6 +208,8 @@ export class SecurityGroupEffects { return 'NOTIFICATIONS.FIREWALL.TEMPLATE_CREATION_DONE'; case SecurityGroupViewMode.Shared: return 'NOTIFICATIONS.FIREWALL.SHARED_GROUP_CREATION_DONE'; + default: + break; } } @@ -197,8 +225,8 @@ export class SecurityGroupEffects { this.dialogService.alert({ message: { translationToken: error.message, - interpolateParams: error.params - } + interpolateParams: error.params, + }, }); } } diff --git a/src/app/reducers/security-groups/redux/sg.reducers.ts b/src/app/reducers/security-groups/redux/sg.reducers.ts index c345818abf..26825ef11c 100644 --- a/src/app/reducers/security-groups/redux/sg.reducers.ts +++ b/src/app/reducers/security-groups/redux/sg.reducers.ts @@ -6,7 +6,7 @@ import { isDefaultSecurityGroup, isSecurityGroupNative, SecurityGroup, - SecurityGroupType + SecurityGroupType, } from '../../../security-group/sg.model'; import * as fromAccounts from '../../accounts/redux/accounts.reducers'; @@ -16,24 +16,24 @@ import { Utils } from '../../../shared/services/utils/utils.service'; import { configSelectors, UserTagsSelectors } from '../../../root-store'; export interface State { - list: ListState, - form: FormState + list: ListState; + form: FormState; } export interface ListState extends EntityState { filters: { - viewMode: string, - selectedAccountIds: string[], - query: string, - selectOrphanSG: boolean - }, - loading: boolean, - selectedSecurityGroupId: string | null + viewMode: string; + selectedAccountIds: string[]; + query: string; + selectOrphanSG: boolean; + }; + loading: boolean; + selectedSecurityGroupId: string | null; } export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: SecurityGroup) => item.id, - sortComparer: Utils.sortByName + sortComparer: Utils.sortByName, }); const initialListState: ListState = adapter.getInitialState({ @@ -41,23 +41,22 @@ const initialListState: ListState = adapter.getInitialState({ query: '', selectedAccountIds: [], viewMode: SecurityGroupViewMode.Templates, - selectOrphanSG: false + selectOrphanSG: false, }, loading: false, - selectedSecurityGroupId: null + selectedSecurityGroupId: null, }); export interface FormState { - loading: boolean, - error: object + loading: boolean; + error: object; } const initialFormState: FormState = { loading: false, - error: null + error: null, }; - export interface SecurityGroupsState { list: ListState; form: FormState; @@ -65,18 +64,18 @@ export interface SecurityGroupsState { export const securityGroupReducers = { list: listReducer, - form: formReducer + form: formReducer, }; export function listReducer( state = initialListState, - action: securityGroupActions.Actions + action: securityGroupActions.Actions, ): ListState { switch (action.type) { case securityGroupActions.LOAD_SECURITY_GROUP_REQUEST: { return { ...state, - loading: true + loading: true, }; } case securityGroupActions.SECURITY_GROUP_FILTER_UPDATE: { @@ -84,8 +83,8 @@ export function listReducer( ...state, filters: { ...state.filters, - ...action.payload - } + ...action.payload, + }, }; } case securityGroupActions.LOAD_SECURITY_GROUP_RESPONSE: { @@ -94,7 +93,7 @@ export function listReducer( case securityGroupActions.LOAD_SELECTED_SECURITY_GROUP: { return { ...state, - selectedSecurityGroupId: action.payload + selectedSecurityGroupId: action.payload, }; } case securityGroupActions.CREATE_SECURITY_GROUP_SUCCESS: { @@ -120,14 +119,14 @@ export function listReducer( export function formReducer( state = initialFormState, - action: securityGroupActions.Actions + action: securityGroupActions.Actions, ): FormState { switch (action.type) { case securityGroupActions.CREATE_SECURITY_GROUP: case securityGroupActions.DELETE_SECURITY_GROUP: { return { ...state, - loading: true + loading: true, }; } case securityGroupActions.CREATE_SECURITY_GROUP_SUCCESS: @@ -136,7 +135,7 @@ export function formReducer( case securityGroupActions.DELETE_SECURITY_GROUP_ERROR: { return { ...state, - loading: false + loading: false, }; } default: { @@ -145,78 +144,54 @@ export function formReducer( } } -export const getSecurityGroupsState = createFeatureSelector( - 'securityGroups'); +export const getSecurityGroupsState = createFeatureSelector('securityGroups'); export const getSecurityGroupsEntitiesState = createSelector( getSecurityGroupsState, - state => state.list + state => state.list, ); export const getSecurityGroupsFormState = createSelector( getSecurityGroupsState, - state => state.form + state => state.form, ); -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getSecurityGroupsEntitiesState); - -export const filters = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getSecurityGroupsEntitiesState, - state => state.filters ); -export const viewMode = createSelector( - filters, - state => state.viewMode -); +export const filters = createSelector(getSecurityGroupsEntitiesState, state => state.filters); -export const query = createSelector( - filters, - state => state.query -); +export const viewMode = createSelector(filters, state => state.viewMode); -export const selectOrphanSG = createSelector( - filters, - state => state.selectOrphanSG -); +export const query = createSelector(filters, state => state.query); -export const filterSelectedAccountIds = createSelector( - filters, - state => state.selectedAccountIds -); +export const selectOrphanSG = createSelector(filters, state => state.selectOrphanSG); + +export const filterSelectedAccountIds = createSelector(filters, state => state.selectedAccountIds); export const getSelectedId = createSelector( getSecurityGroupsEntitiesState, - state => state.selectedSecurityGroupId + state => state.selectedSecurityGroupId, ); export const getSelectedSecurityGroup = createSelector( getSecurityGroupsEntitiesState, getSelectedId, - (state, selectedId) => state.entities[selectedId] + (state, selectedId) => state.entities[selectedId], ); -export const isListLoading = createSelector( - getSecurityGroupsEntitiesState, - state => state.loading -); +export const isListLoading = createSelector(getSecurityGroupsEntitiesState, state => state.loading); -export const isFormLoading = createSelector( - getSecurityGroupsFormState, - state => state.loading -); +export const isFormLoading = createSelector(getSecurityGroupsFormState, state => state.loading); const selectDefaultSecurityGroupName = createSelector( configSelectors.get('defaultSecurityGroupName'), UserTagsSelectors.getInterfaceLanguage, (names, lang) => { return names[lang]; - }); + }, +); export const selectFilteredSecurityGroups = createSelector( selectAll, @@ -226,71 +201,84 @@ export const selectFilteredSecurityGroups = createSelector( (securityGroups, filter, accounts, defaultSecurityGroupName) => { const mode = filter.viewMode; const queryLower = filter.query ? filter.query.toLowerCase() : ''; - const queryFilter = (group: SecurityGroup) => !queryLower || group.name.toLowerCase() - .includes(queryLower); + const queryFilter = (group: SecurityGroup) => + !queryLower || group.name.toLowerCase().includes(queryLower); - const selectedAccounts = accounts.filter( - account => filter.selectedAccountIds.find(id => id === account.id)); + const selectedAccounts = accounts.filter(account => + filter.selectedAccountIds.find(id => id === account.id), + ); const accountsMap = selectedAccounts.reduce((m, i) => ({ ...m, [i.name]: i }), {}); const domainsMap = selectedAccounts.reduce((m, i) => ({ ...m, [i.domainid]: i }), {}); - const selectedAccountIdsFilter = group => !filter.selectedAccountIds.length || + const selectedAccountIdsFilter = group => + !filter.selectedAccountIds.length || group.type === SecurityGroupType.PredefinedTemplate || (accountsMap[group.account] && domainsMap[group.domainid]); const viewModeFilter = (group: SecurityGroup) => { if (mode === SecurityGroupViewMode.Templates) { - return getType(group) === SecurityGroupType.PredefinedTemplate - || getType(group) === SecurityGroupType.CustomTemplate; - } else if (mode === SecurityGroupViewMode.Shared) { + return ( + getType(group) === SecurityGroupType.PredefinedTemplate || + getType(group) === SecurityGroupType.CustomTemplate + ); + } + if (mode === SecurityGroupViewMode.Shared) { return getType(group) === SecurityGroupType.Shared; - } else if (mode === SecurityGroupViewMode.Private) { + } + if (mode === SecurityGroupViewMode.Private) { return getType(group) === SecurityGroupType.Private; } }; - const isOrphan = (group: SecurityGroup) => ( - filter.selectOrphanSG - && mode === SecurityGroupViewMode.Private - && isSecurityGroupNative(group) - ) ? group.virtualmachineids.length === 0 : true; + const isOrphan = (group: SecurityGroup) => + filter.selectOrphanSG && + mode === SecurityGroupViewMode.Private && + isSecurityGroupNative(group) + ? group.virtualmachineids.length === 0 + : true; const renameDefaultSG = (securityGroup: SecurityGroup) => { - return isDefaultSecurityGroup(securityGroup) ? {...securityGroup, name: defaultSecurityGroupName} : securityGroup; + return isDefaultSecurityGroup(securityGroup) + ? { ...securityGroup, name: defaultSecurityGroupName } + : securityGroup; }; return securityGroups - .map(sg => renameDefaultSG(sg)) - .filter(group => queryFilter(group) - && viewModeFilter(group) - && selectedAccountIdsFilter(group) - && isOrphan(group)); - }); + .map(renameDefaultSG) + .filter( + group => + queryFilter(group) && + viewModeFilter(group) && + selectedAccountIdsFilter(group) && + isOrphan(group), + ); + }, +); export const selectSecurityGroupsForVmCreation = createSelector( selectAll, fromAuth.getUserAccount, selectDefaultSecurityGroupName, (securityGroups, account, defaultSecurityGroupName) => { - const accountFilter = (securityGroup: SecurityGroup) => ( - account - && isSecurityGroupNative(securityGroup) - && securityGroup.account === account.name - ); + const accountFilter = (securityGroup: SecurityGroup) => + account && isSecurityGroupNative(securityGroup) && securityGroup.account === account.name; const onlySharedFilter = (securityGroup: SecurityGroup) => getType(securityGroup) === SecurityGroupType.Shared; const renameDefaultSG = (securityGroup: SecurityGroup) => { - return isDefaultSecurityGroup(securityGroup) ? {...securityGroup, name: defaultSecurityGroupName} : securityGroup; + return isDefaultSecurityGroup(securityGroup) + ? { ...securityGroup, name: defaultSecurityGroupName } + : securityGroup; }; return securityGroups - .map(sg => renameDefaultSG(sg)) - .filter((securityGroup) => accountFilter(securityGroup) && onlySharedFilter(securityGroup)); - }); + .map(renameDefaultSG) + .filter(securityGroup => accountFilter(securityGroup) && onlySharedFilter(securityGroup)); + }, +); export const selectPredefinedSecurityGroups = createSelector( selectAll, - (securityGroups: SecurityGroup[]) => securityGroups.filter( - securityGroup => securityGroup.preselected) + (securityGroups: SecurityGroup[]) => + securityGroups.filter(securityGroup => securityGroup.preselected), ); export const selectDefaultSecurityGroup = createSelector( @@ -298,10 +286,10 @@ export const selectDefaultSecurityGroup = createSelector( selectDefaultSecurityGroupName, fromAuth.getUserAccount, (securityGroups, defaultSecurityGroupName, user) => { - const defaultGroup = securityGroups.find((sg: SecurityGroup) => ( - isSecurityGroupNative(sg) - && sg.account === user.name - && sg.name === 'default' - )); + const defaultGroup = securityGroups.find( + (sg: SecurityGroup) => + isSecurityGroupNative(sg) && sg.account === user.name && sg.name === 'default', + ); return { ...defaultGroup, name: defaultSecurityGroupName }; - }); + }, +); diff --git a/src/app/reducers/service-offerings/redux/service-offerings.actions.ts b/src/app/reducers/service-offerings/redux/service-offerings.actions.ts index 12aba0a84b..906328dd2a 100644 --- a/src/app/reducers/service-offerings/redux/service-offerings.actions.ts +++ b/src/app/reducers/service-offerings/redux/service-offerings.actions.ts @@ -5,28 +5,22 @@ export const LOAD_SERVICE_OFFERINGS_REQUEST = '[OFFERINGS] LOAD_SERVICE_OFFERING export const LOAD_SERVICE_OFFERINGS_RESPONSE = '[OFFERINGS] LOAD_SERVICE_OFFERINGS_RESPONSE'; export const SERVICE_OFFERINGS_FILTER_UPDATE = '[OFFERINGS] SERVICE_OFFERINGS_FILTER_UPDATE'; - export class LoadOfferingsRequest implements Action { type = LOAD_SERVICE_OFFERINGS_REQUEST; - constructor(public payload?: any) { - } + constructor(public payload?: any) {} } export class LoadOfferingsResponse implements Action { type = LOAD_SERVICE_OFFERINGS_RESPONSE; - constructor(public payload: ServiceOffering[]) { - } + constructor(public payload: ServiceOffering[]) {} } export class ServiceOfferingsFilterUpdate implements Action { type = SERVICE_OFFERINGS_FILTER_UPDATE; - constructor(public payload: any) { - } + constructor(public payload: any) {} } -export type Actions = - | LoadOfferingsResponse - | LoadOfferingsRequest; +export type Actions = LoadOfferingsResponse | LoadOfferingsRequest; diff --git a/src/app/reducers/service-offerings/redux/service-offerings.effects.ts b/src/app/reducers/service-offerings/redux/service-offerings.effects.ts index 316ff69ef4..5f3ac27c63 100644 --- a/src/app/reducers/service-offerings/redux/service-offerings.effects.ts +++ b/src/app/reducers/service-offerings/redux/service-offerings.effects.ts @@ -18,12 +18,10 @@ export class ServiceOfferingEffects { map((offerings: ServiceOffering[]) => { return new serviceOfferingActions.LoadOfferingsResponse(offerings); }), - catchError(() => of(new serviceOfferingActions.LoadOfferingsResponse([])))); - })); + catchError(() => of(new serviceOfferingActions.LoadOfferingsResponse([]))), + ); + }), + ); - constructor( - private actions$: Actions, - private offeringService: ServiceOfferingService, - ) { - } + constructor(private actions$: Actions, private offeringService: ServiceOfferingService) {} } diff --git a/src/app/reducers/service-offerings/redux/service-offerings.reducers.spec.ts b/src/app/reducers/service-offerings/redux/service-offerings.reducers.spec.ts index 205df42a2d..dec02480e2 100644 --- a/src/app/reducers/service-offerings/redux/service-offerings.reducers.spec.ts +++ b/src/app/reducers/service-offerings/redux/service-offerings.reducers.spec.ts @@ -1,78 +1,78 @@ -import { ServiceOfferingType } from '../../../shared/models'; -import { LOAD_SERVICE_OFFERINGS_REQUEST, LOAD_SERVICE_OFFERINGS_RESPONSE } from './service-offerings.actions'; +import { serviceOfferingType } from '../../../shared/models'; +import { + LOAD_SERVICE_OFFERINGS_REQUEST, + LOAD_SERVICE_OFFERINGS_RESPONSE, +} from './service-offerings.actions'; import * as fromSOs from './service-offerings.reducers'; describe('Test service offering reducer', () => { it('should handle initial state', () => { const state = fromSOs.reducer(undefined, { type: '' }); - expect(state) - .toEqual({ - ids: [], - entities: {}, - loading: false, - filters: { - selectedViewMode: ServiceOfferingType.fixed, - selectedClasses: [], - query: '' - } - }); + expect(state).toEqual({ + ids: [], + entities: {}, + loading: false, + filters: { + selectedViewMode: serviceOfferingType.fixed, + selectedClasses: [], + query: '', + }, + }); }); it('should set loading', () => { const state = fromSOs.reducer(undefined, { type: LOAD_SERVICE_OFFERINGS_REQUEST }); - expect(state) - .toEqual({ - ids: [], - entities: {}, - loading: true, - filters: { - selectedViewMode: ServiceOfferingType.fixed, - selectedClasses: [], - query: '' - } - }); + expect(state).toEqual({ + ids: [], + entities: {}, + loading: true, + filters: { + selectedViewMode: serviceOfferingType.fixed, + selectedClasses: [], + query: '', + }, + }); }); it('should set entities', () => { const state = fromSOs.reducer(undefined, { type: LOAD_SERVICE_OFFERINGS_RESPONSE, - payload: [{ id: '1', name: 'off1' }, { id: '2', name: 'off2' }] + payload: [{ id: '1', name: 'off1' }, { id: '2', name: 'off2' }], }); - expect(state) - .toEqual({ - ids: ['1', '2'], - entities: { 1: { id: '1', name: 'off1' }, 2: { id: '2', name: 'off2' } }, - loading: false, - filters: { - selectedViewMode: ServiceOfferingType.fixed, - selectedClasses: [], - query: '' - } - }); + expect(state).toEqual({ + ids: ['1', '2'], + entities: { 1: { id: '1', name: 'off1' }, 2: { id: '2', name: 'off2' } }, + loading: false, + filters: { + selectedViewMode: serviceOfferingType.fixed, + selectedClasses: [], + query: '', + }, + } as any); }); it('should get state', () => { const state = fromSOs.reducer(undefined, { type: LOAD_SERVICE_OFFERINGS_RESPONSE, - payload: [{ id: '1', name: 'off1' }, { id: '2', name: 'off2' }] + payload: [{ id: '1', name: 'off1' }, { id: '2', name: 'off2' }], }); - expect(state) - .toEqual({ - ids: ['1', '2'], - entities: { 1: { id: '1', name: 'off1' }, 2: { id: '2', name: 'off2' } }, - loading: false, - filters: { - selectedViewMode: ServiceOfferingType.fixed, - selectedClasses: [], - query: '' - } - }); + expect(state).toEqual({ + ids: ['1', '2'], + entities: { 1: { id: '1', name: 'off1' }, 2: { id: '2', name: 'off2' } }, + loading: false, + filters: { + selectedViewMode: serviceOfferingType.fixed, + selectedClasses: [], + query: '', + }, + } as any); expect(fromSOs.getOfferingsEntitiesState.projector({ list: state })).toBe(state); expect(fromSOs.isLoading.projector(state)).toBe(false); - expect(fromSOs.getSelectedOffering.projector( - state.entities, - { serviceOfferingId: 1 } - )) - .toEqual({ id: '1', name: 'off1' }); + expect(fromSOs.getSelectedOffering.projector(state.entities, { serviceofferingid: 1 })).toEqual( + { + id: '1', + name: 'off1', + }, + ); }); }); diff --git a/src/app/reducers/service-offerings/redux/service-offerings.reducers.ts b/src/app/reducers/service-offerings/redux/service-offerings.reducers.ts index 688d7e525f..62734e9a25 100644 --- a/src/app/reducers/service-offerings/redux/service-offerings.reducers.ts +++ b/src/app/reducers/service-offerings/redux/service-offerings.reducers.ts @@ -5,19 +5,18 @@ import { ComputeOfferingClass, defaultComputeOfferingClass, ServiceOffering, - ServiceOfferingType + serviceOfferingType, } from '../../../shared/models'; import * as fromVMs from '../../vm/redux/vm.reducers'; import * as serviceOfferingActions from './service-offerings.actions'; - export interface State extends EntityState { loading: boolean; filters: { - selectedViewMode: string, - selectedClasses: string[], - query: string - } + selectedViewMode: string; + selectedClasses: string[]; + query: string; + }; } export interface OfferingsState { @@ -28,21 +27,20 @@ export const serviceOfferingReducers = { list: reducer, }; -export const adapter: EntityAdapter = createEntityAdapter( - { - selectId: (item: ServiceOffering) => item.id, - sortComparer: false - }); +export const adapter: EntityAdapter = createEntityAdapter({ + selectId: (item: ServiceOffering) => item.id, + sortComparer: false, +}); export const initialFilters = { - selectedViewMode: ServiceOfferingType.fixed, + selectedViewMode: serviceOfferingType.fixed, selectedClasses: [], - query: '' + query: '', }; export const initialState: State = adapter.getInitialState({ loading: false, - filters: initialFilters + filters: initialFilters, }); export function reducer(state = initialState, action: serviceOfferingActions.Actions): State { @@ -50,7 +48,7 @@ export function reducer(state = initialState, action: serviceOfferingActions.Act case serviceOfferingActions.LOAD_SERVICE_OFFERINGS_REQUEST: { return { ...state, - loading: true + loading: true, }; } case serviceOfferingActions.SERVICE_OFFERINGS_FILTER_UPDATE: { @@ -58,15 +56,15 @@ export function reducer(state = initialState, action: serviceOfferingActions.Act ...state, filters: { ...state.filters, - ...action.payload - } + ...action.payload, + }, }; } case serviceOfferingActions.LOAD_SERVICE_OFFERINGS_RESPONSE: { const offerings = action.payload; return { ...adapter.addAll(offerings, state), - loading: false + loading: false, }; } @@ -76,56 +74,41 @@ export function reducer(state = initialState, action: serviceOfferingActions.Act } } - export const getOfferingsState = createFeatureSelector('service-offerings'); -export const getOfferingsEntitiesState = createSelector( - getOfferingsState, - state => state.list -); - -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getOfferingsEntitiesState); +export const getOfferingsEntitiesState = createSelector(getOfferingsState, state => state.list); -export const isLoading = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getOfferingsEntitiesState, - state => state.loading ); -export const filters = createSelector( - getOfferingsEntitiesState, - state => state.filters -); +export const isLoading = createSelector(getOfferingsEntitiesState, state => state.loading); -export const filterSelectedViewMode = createSelector( - filters, - state => state.selectedViewMode -); +export const filters = createSelector(getOfferingsEntitiesState, state => state.filters); -export const filterSelectedClasses = createSelector( - filters, - state => state.selectedClasses -); +export const filterSelectedViewMode = createSelector(filters, state => state.selectedViewMode); -export const filterQuery = createSelector( - filters, - state => state.query -); +export const filterSelectedClasses = createSelector(filters, state => state.selectedClasses); + +export const filterQuery = createSelector(filters, state => state.query); export const getSelectedOffering = createSelector( selectEntities, fromVMs.getSelectedVM, - (entities, vm) => vm && entities[vm.serviceOfferingId] + (entities, vm) => vm && entities[vm.serviceofferingid], ); -export const classesFilter = (offering: ServiceOffering, soClasses: ComputeOfferingClass[], classesMap: any) => { - const classes = soClasses.filter(soClass => - soClass.computeOfferings && soClass.computeOfferings.indexOf(offering.id) > -1); +export const classesFilter = ( + offering: ServiceOffering, + soClasses: ComputeOfferingClass[], + classesMap: any, +) => { + const classes = soClasses.filter( + soClass => soClass.computeOfferings && soClass.computeOfferings.indexOf(offering.id) > -1, + ); const showGeneral = !!classesMap[defaultComputeOfferingClass.id]; - return classes.length && classes.find(soClass => classesMap[soClass.id]) - || (showGeneral && !classes.length); + return ( + (classes.length && classes.find(soClass => classesMap[soClass.id])) || + (showGeneral && !classes.length) + ); }; diff --git a/src/app/reducers/snapshots/redux/snapshot.actions.ts b/src/app/reducers/snapshots/redux/snapshot.actions.ts index 4e87439d45..a91c5e0ed4 100644 --- a/src/app/reducers/snapshots/redux/snapshot.actions.ts +++ b/src/app/reducers/snapshots/redux/snapshot.actions.ts @@ -20,89 +20,77 @@ export const SNAPSHOT_UPDATE_ERROR = '[Snapshots] SNAPSHOT_UPDATE_ERROR'; export class LoadSnapshotRequest implements Action { readonly type = LOAD_SNAPSHOT_REQUEST; - constructor(public payload?: any) { - } + constructor(public payload?: any) {} } export class LoadSnapshotResponse implements Action { readonly type = LOAD_SNAPSHOT_RESPONSE; - constructor(public payload: Snapshot[]) { - } + constructor(public payload: Snapshot[]) {} } export class SnapshotFilterUpdate implements Action { readonly type = SNAPSHOT_FILTER_UPDATE; - constructor(public payload: any) { - } + constructor(public payload: any) {} } export class AddSnapshot implements Action { readonly type = ADD_SNAPSHOT; - constructor(public payload: Volume) { - } + constructor(public payload: Volume) {} } export class DeleteSnapshot implements Action { readonly type = DELETE_SNAPSHOT; - constructor(public payload: Snapshot) { - } + constructor(public payload: Snapshot) {} } export class DeleteSnapshots implements Action { readonly type = DELETE_SNAPSHOTS; - constructor(public payload: Array) { - } + constructor(public payload: Snapshot[]) {} } export class AddSnapshotSuccess implements Action { readonly type = ADD_SNAPSHOT_SUCCESS; - constructor(public payload: Snapshot) { - } + constructor(public payload: Snapshot) {} } export class DeleteSnapshotSuccess implements Action { readonly type = DELETE_SNAPSHOT_SUCCESS; - constructor(public payload: Snapshot) { - } + constructor(public payload: Snapshot) {} } export class SnapshotUpdateError implements Action { readonly type = SNAPSHOT_UPDATE_ERROR; - constructor(public payload: Error) { - } + constructor(public payload: Error) {} } export class RevertVolumeToSnapshot implements Action { readonly type = REVERT_VOLUME_TO_SNAPSHOT; - constructor(public payload: Snapshot) { - } + constructor(public payload: Snapshot) {} } export class RevertVolumeToSnapshotSuccess implements Action { readonly type = REVERT_VOLUME_TO_SNAPSHOT_SUCCESS; - constructor(public payload: Snapshot) { - } + constructor(public payload: Snapshot) {} } export class LoadSelectedSnapshot implements Action { readonly type = LOAD_SELECTED_SNAPSHOT; - constructor(public payload: string) { - } + constructor(public payload: string) {} } export type Actions = - LoadSnapshotRequest + | LoadSnapshotRequest | LoadSnapshotResponse | SnapshotFilterUpdate | AddSnapshot @@ -113,4 +101,3 @@ export type Actions = | RevertVolumeToSnapshot | RevertVolumeToSnapshotSuccess | LoadSelectedSnapshot; - diff --git a/src/app/reducers/snapshots/redux/snapshot.effects.spec.ts b/src/app/reducers/snapshots/redux/snapshot.effects.spec.ts index 17d6211047..afefc10fe3 100644 --- a/src/app/reducers/snapshots/redux/snapshot.effects.spec.ts +++ b/src/app/reducers/snapshots/redux/snapshot.effects.spec.ts @@ -25,7 +25,7 @@ import * as snapshotActions from './snapshot.actions'; import { SnapshotEffects } from './snapshot.effects'; import * as fromSnapshots from './snapshot.reducers'; -const snapshots: Array = [ +const snapshots: Snapshot[] = [ { description: 'test snapshot', id: 'test-id', @@ -37,14 +37,13 @@ const snapshots: Array = [ tags: [], state: SnapshotStates.BackedUp, revertable: false, - snapshottype: SnapshotType.Manual - } + snapshottype: SnapshotType.Manual, + }, ]; @Injectable() class MockAsyncJobService { - public completeAllJobs(): void { - } + public completeAllJobs(): void {} } @Injectable() @@ -69,17 +68,15 @@ export class MockVmEffects { class MockMatDialog { public open() { return { - afterClosed: () => of(snapshots[0]) + afterClosed: () => of(snapshots[0]), }; } - public closeAll(): void { - } + public closeAll(): void {} } @Injectable() -export class MockVmService { -} +export class MockVmService {} export class TestActions extends Actions { constructor() { @@ -87,6 +84,8 @@ export class TestActions extends Actions { } public set stream(source: Observable) { + // todo + // tslint:disable-next-line this.source = source; } } @@ -95,43 +94,34 @@ export function getActions() { return new TestActions(); } -const snapList: Array = require( - '../../../../testutils/mocks/model-services/fixtures/snapshots.json'); - +const snapList: Snapshot[] = require('../../../../testutils/mocks/model-services/fixtures/snapshots.json'); describe('Snapshot Effects', () => { let actions$: TestActions; let service: SnapshotService; let effects: SnapshotEffects; let dialogService: DialogService; - let jobNotificationService: JobsNotificationService; - const list: Array = snapList; + const list: Snapshot[] = snapList; - const jobsNotificationService = jasmine.createSpyObj( - 'JobsNotificationService', - { - 'add': 'notification-id', - 'finish': 'notification-id', - 'fail': 'notification-id' - } - ); - - const MockVirtualMachinesEffects = jasmine.createSpyObj( - 'VirtualMachinesEffects', ['stop'] - ); + const jobsNotificationService = jasmine.createSpyObj('JobsNotificationService', { + add: 'notification-id', + finish: 'notification-id', + fail: 'notification-id', + }); + const mockVirtualMachinesEffects = jasmine.createSpyObj('VirtualMachinesEffects', ['stop']); beforeEach(() => { TestBed.configureTestingModule({ imports: [ HttpClientTestingModule, - StoreModule.forRoot({ ...fromSnapshots.snapshotReducers }) + StoreModule.forRoot({ ...fromSnapshots.snapshotReducers }), ], providers: [ SnapshotService, SnapshotEffects, - { provide: VirtualMachinesEffects, useValue: MockVirtualMachinesEffects }, + { provide: VirtualMachinesEffects, useValue: mockVirtualMachinesEffects }, { provide: Actions, useFactory: getActions }, { provide: AuthService, useClass: MockAuthService }, { provide: AsyncJobService, useClass: MockAsyncJobService }, @@ -140,14 +130,13 @@ describe('Snapshot Effects', () => { { provide: JobsNotificationService, useValue: jobsNotificationService }, { provide: DialogService, useClass: MockDialogService }, { provide: MatDialog, useClass: MockMatDialog }, - { provide: Router, useClass: MockRouter } - ] + { provide: Router, useClass: MockRouter }, + ], }); actions$ = TestBed.get(Actions); service = TestBed.get(SnapshotService); effects = TestBed.get(SnapshotEffects); dialogService = TestBed.get(DialogService); - jobNotificationService = TestBed.get(JobsNotificationService); }); it('should return a collection from LoadSnapshotResponse', () => { @@ -163,9 +152,9 @@ describe('Snapshot Effects', () => { }); it('should return an empty collection with error from LoadSnapshotResponse', () => { - const spyGetList = spyOn(service, 'getListAll') - .and - .returnValue(throwError(new Error('Error occurred!'))); + const spyGetList = spyOn(service, 'getListAll').and.returnValue( + throwError(new Error('Error occurred!')), + ); const action = new snapshotActions.LoadSnapshotRequest(); const completion = new snapshotActions.LoadSnapshotResponse([]); @@ -188,15 +177,15 @@ describe('Snapshot Effects', () => { tags: [], state: SnapshotStates.BackedUp, revertable: true, - snapshottype: SnapshotType.Manual + snapshottype: SnapshotType.Manual, }; const spyCommand = spyOn(service, 'create').and.returnValue(of(newSnapshot)); spyOn(dialogService, 'confirm').and.returnValue(of(true)); - const action = new snapshotActions.AddSnapshot({ - id: 'volume-id' - }); + const action = new snapshotActions.AddSnapshot({ + id: 'volume-id', + } as Volume); const completion = new snapshotActions.AddSnapshotSuccess(newSnapshot); actions$.stream = hot('-a', { a: action }); @@ -208,12 +197,12 @@ describe('Snapshot Effects', () => { }); it('should catch error while adding a new snapshot', () => { - const spyCommand = spyOn(service, 'create') - .and - .returnValue(throwError(new Error('Error occurred!'))); + const spyCommand = spyOn(service, 'create').and.returnValue( + throwError(new Error('Error occurred!')), + ); spyOn(dialogService, 'confirm').and.returnValue(of(true)); - const action = new snapshotActions.AddSnapshot({ id: 'volume-id' }); + const action = new snapshotActions.AddSnapshot({ id: 'volume-id' } as Volume); const completion = new snapshotActions.SnapshotUpdateError(new Error('Error occurred!')); actions$.stream = hot('-a', { a: action }); @@ -239,9 +228,9 @@ describe('Snapshot Effects', () => { }); it('should catch error while removing a snapshot', () => { - const spyCommand = spyOn(service, 'remove') - .and - .returnValue(throwError(new Error('Error occurred!'))); + const spyCommand = spyOn(service, 'remove').and.returnValue( + throwError(new Error('Error occurred!')), + ); spyOn(dialogService, 'confirm').and.returnValue(of(true)); const action = new snapshotActions.DeleteSnapshot(snapshots[0]); diff --git a/src/app/reducers/snapshots/redux/snapshot.effects.ts b/src/app/reducers/snapshots/redux/snapshot.effects.ts index 7494be0be7..b496f388bd 100644 --- a/src/app/reducers/snapshots/redux/snapshot.effects.ts +++ b/src/app/reducers/snapshots/redux/snapshot.effects.ts @@ -4,10 +4,19 @@ import { Router } from '@angular/router'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { Action, select, Store } from '@ngrx/store'; import { Observable, of } from 'rxjs'; -import { catchError, filter, map, mergeMap, onErrorResumeNext, switchMap, tap, withLatestFrom } from 'rxjs/operators'; +import { + catchError, + filter, + map, + mergeMap, + onErrorResumeNext, + switchMap, + tap, + withLatestFrom, +} from 'rxjs/operators'; import { DialogService } from '../../../dialog/dialog-service/dialog.service'; -import { ISnapshotData, Snapshot, Volume } from '../../../shared/models'; +import { SnapshotData, Snapshot, Volume } from '../../../shared/models'; import { JobsNotificationService } from '../../../shared/services/jobs-notification.service'; import { NgrxEntities } from '../../../shared/interfaces'; import { SnackBarService } from '../../../core/services'; @@ -22,39 +31,36 @@ import * as fromVMs from '../../vm/redux/vm.reducers'; import * as fromVolumes from '../../volumes/redux/volumes.reducers'; import * as snapshotActions from './snapshot.actions'; - @Injectable() export class SnapshotEffects { @Effect() loadSnapshots$: Observable = this.actions$.pipe( ofType(snapshotActions.LOAD_SNAPSHOT_REQUEST), switchMap((action: snapshotActions.LoadSnapshotRequest) => { - return this.snapshotService - .getListAll(action.payload).pipe( - map((snapshots: Snapshot[]) => new snapshotActions.LoadSnapshotResponse(snapshots)), - catchError(() => of(new snapshotActions.LoadSnapshotResponse([])))); - })); - + return this.snapshotService.getListAll(action.payload).pipe( + map((snapshots: Snapshot[]) => new snapshotActions.LoadSnapshotResponse(snapshots)), + catchError(() => of(new snapshotActions.LoadSnapshotResponse([]))), + ); + }), + ); @Effect() addSnapshot$: Observable = this.actions$.pipe( ofType(snapshotActions.ADD_SNAPSHOT), mergeMap((action: snapshotActions.AddSnapshot) => { - return this.dialog.open(SnapshotCreationComponent, { - data: action.payload - }) - .afterClosed().pipe( - filter(res => Boolean(res)), - mergeMap((params: ISnapshotData) => { - + return this.dialog + .open(SnapshotCreationComponent, { + data: action.payload, + }) + .afterClosed() + .pipe( + filter(Boolean), + mergeMap((params: SnapshotData) => { const notificationId = this.jobsNotificationService.add( - 'NOTIFICATIONS.SNAPSHOT.TAKE_IN_PROGRESS'); + 'NOTIFICATIONS.SNAPSHOT.TAKE_IN_PROGRESS', + ); - return this.snapshotService.create( - action.payload.id, - params.name, - params.desc - ).pipe( + return this.snapshotService.create(action.payload.id, params.name, params.desc).pipe( tap(() => { const message = 'NOTIFICATIONS.SNAPSHOT.TAKE_DONE'; this.showNotificationsOnFinish(message, notificationId); @@ -62,20 +68,24 @@ export class SnapshotEffects { map(newSnap => { return new snapshotActions.AddSnapshotSuccess(newSnap); }), - catchError((error) => { + catchError(error => { const message = 'NOTIFICATIONS.SNAPSHOT.TAKE_FAILED'; this.showNotificationsOnFail(error, message, notificationId); return of(new snapshotActions.SnapshotUpdateError(error)); - })); - })); - })); + }), + ); + }), + ); + }), + ); @Effect() deleteSnapshot$: Observable = this.actions$.pipe( ofType(snapshotActions.DELETE_SNAPSHOT), mergeMap((action: snapshotActions.DeleteSnapshot) => { const notificationId = this.jobsNotificationService.add( - 'NOTIFICATIONS.SNAPSHOT.DELETION_IN_PROGRESS'); + 'NOTIFICATIONS.SNAPSHOT.DELETION_IN_PROGRESS', + ); return this.snapshotService.remove(action.payload.id).pipe( tap(() => { const message = 'NOTIFICATIONS.SNAPSHOT.DELETION_DONE'; @@ -84,80 +94,96 @@ export class SnapshotEffects { map(() => { return new snapshotActions.DeleteSnapshotSuccess(action.payload); }), - catchError((error) => { + catchError(error => { const message = 'NOTIFICATIONS.SNAPSHOT.DELETION_FAILED'; this.showNotificationsOnFail(error, message, notificationId); return of(new snapshotActions.SnapshotUpdateError(error)); - })); - })); + }), + ); + }), + ); @Effect() deleteSnapshots$: Observable = this.actions$.pipe( ofType(snapshotActions.DELETE_SNAPSHOTS), - mergeMap((action: snapshotActions.DeleteSnapshots) => action.payload - .map((snapshot: Snapshot) => new snapshotActions.DeleteSnapshot(snapshot)))); + mergeMap((action: snapshotActions.DeleteSnapshots) => + action.payload.map((snapshot: Snapshot) => new snapshotActions.DeleteSnapshot(snapshot)), + ), + ); @Effect() revertVolumeToSnapshot$: Observable = this.actions$.pipe( ofType(snapshotActions.REVERT_VOLUME_TO_SNAPSHOT), withLatestFrom( this.store.pipe(select(fromVolumes.selectEntities)), - this.store.pipe(select(fromVMs.selectEntities)) + this.store.pipe(select(fromVMs.selectEntities)), ), - mergeMap(([action, volumes, vms]: [ - snapshotActions.RevertVolumeToSnapshot, NgrxEntities, NgrxEntities + mergeMap( + ([action, volumes, vms]: [ + snapshotActions.RevertVolumeToSnapshot, + NgrxEntities, + NgrxEntities ]) => { - const vmId = Object.entries(volumes) - && volumes[action.payload.volumeid] - && volumes[action.payload.volumeid].virtualmachineid; - const isVmRunning = Object.entries(vms).length && vms[vmId] && vms[vmId].state === VmState.Running; - - return this.dialogService.confirm({ - message: isVmRunning - ? 'DIALOG_MESSAGES.SNAPSHOT.CONFIRM_REVERTING_WITH_VM_REBOOT' - : 'DIALOG_MESSAGES.SNAPSHOT.CONFIRM_REVERTING' - }).pipe( - onErrorResumeNext(), - filter(res => Boolean(res)), - mergeMap(() => (isVmRunning - ? this.vmEffects.stop(vms[vmId]) - : of(null)).pipe( - mergeMap(() => { - const notificationId = this.jobsNotificationService.add( - 'NOTIFICATIONS.SNAPSHOT.REVERT_IN_PROGRESS'); - return this.snapshotService.revert(action.payload.id).pipe( - tap(() => { - const message = 'NOTIFICATIONS.SNAPSHOT.REVERT_DONE'; - this.showNotificationsOnFinish(message, notificationId); - }), - mergeMap(() => { - return isVmRunning - ? [ - new snapshotActions.RevertVolumeToSnapshotSuccess(action.payload), - new vmActions.StartVm(vms[vmId]) - ] - : [new snapshotActions.RevertVolumeToSnapshotSuccess(action.payload)]; - }), - catchError((error) => { - const message = 'NOTIFICATIONS.SNAPSHOT.REVERT_FAILED'; - this.showNotificationsOnFail(error, message, notificationId); - return of(new snapshotActions.SnapshotUpdateError(error)) - })); - })))); - })); + const vmId = + Object.entries(volumes) && + volumes[action.payload.volumeid] && + volumes[action.payload.volumeid].virtualmachineid; + const isVmRunning = + Object.entries(vms).length && vms[vmId] && vms[vmId].state === VmState.Running; + + return this.dialogService + .confirm({ + message: isVmRunning + ? 'DIALOG_MESSAGES.SNAPSHOT.CONFIRM_REVERTING_WITH_VM_REBOOT' + : 'DIALOG_MESSAGES.SNAPSHOT.CONFIRM_REVERTING', + }) + .pipe( + onErrorResumeNext(), + filter(Boolean), + mergeMap(() => + (isVmRunning ? this.vmEffects.stop(vms[vmId]) : of(null)).pipe( + mergeMap(() => { + const notificationId = this.jobsNotificationService.add( + 'NOTIFICATIONS.SNAPSHOT.REVERT_IN_PROGRESS', + ); + return this.snapshotService.revert(action.payload.id).pipe( + tap(() => { + const message = 'NOTIFICATIONS.SNAPSHOT.REVERT_DONE'; + this.showNotificationsOnFinish(message, notificationId); + }), + mergeMap(() => { + return isVmRunning + ? [ + new snapshotActions.RevertVolumeToSnapshotSuccess(action.payload), + new vmActions.StartVm(vms[vmId]), + ] + : [new snapshotActions.RevertVolumeToSnapshotSuccess(action.payload)]; + }), + catchError(error => { + const message = 'NOTIFICATIONS.SNAPSHOT.REVERT_FAILED'; + this.showNotificationsOnFail(error, message, notificationId); + return of(new snapshotActions.SnapshotUpdateError(error)); + }), + ); + }), + ), + ), + ); + }, + ), + ); @Effect({ dispatch: false }) deleteSnapshotSuccess$ = this.actions$.pipe( ofType(snapshotActions.DELETE_SNAPSHOT_SUCCESS), map((action: snapshotActions.DeleteSnapshotSuccess) => action.payload), - filter((snapshot: Snapshot) => this.router.isActive( - `/snapshots/${snapshot.id}`, - false - )), - tap(() => this.router.navigate(['./snapshots'], { - queryParamsHandling: 'preserve' - }))); - + filter((snapshot: Snapshot) => this.router.isActive(`/snapshots/${snapshot.id}`, false)), + tap(() => + this.router.navigate(['./snapshots'], { + queryParamsHandling: 'preserve', + }), + ), + ); constructor( private store: Store, @@ -168,15 +194,14 @@ export class SnapshotEffects { private dialog: MatDialog, private vmEffects: VirtualMachinesEffects, private router: Router, - private snackBarService: SnackBarService - ) { - } + private snackBarService: SnackBarService, + ) {} private showNotificationsOnFinish(message: string, jobNotificationId?: string) { if (jobNotificationId) { this.jobsNotificationService.finish({ + message, id: jobNotificationId, - message }); } this.snackBarService.open(message).subscribe(); @@ -185,15 +210,15 @@ export class SnapshotEffects { private showNotificationsOnFail(error: any, message?: string, jobNotificationId?: string) { if (jobNotificationId) { this.jobsNotificationService.fail({ + message, id: jobNotificationId, - message }); } this.dialogService.alert({ message: { translationToken: error.message, - interpolateParams: error.params - } + interpolateParams: error.params, + }, }); } } diff --git a/src/app/reducers/snapshots/redux/snapshot.reducers.spec.ts b/src/app/reducers/snapshots/redux/snapshot.reducers.spec.ts index e162a77e8a..a53ec9564d 100644 --- a/src/app/reducers/snapshots/redux/snapshot.reducers.spec.ts +++ b/src/app/reducers/snapshots/redux/snapshot.reducers.spec.ts @@ -15,8 +15,9 @@ describe('Snapshot Reducer', () => { tags: [], state: SnapshotStates.BackedUp, revertable: true, - snapshottype: SnapshotType.Manual - }, { + snapshottype: SnapshotType.Manual, + }, + { description: 'test snapshot #2', id: '2', created: '2018-01-10T15:59:42+0700', @@ -27,8 +28,8 @@ describe('Snapshot Reducer', () => { tags: [], state: SnapshotStates.BackedUp, revertable: false, - snapshottype: SnapshotType.Manual - } + snapshottype: SnapshotType.Manual, + }, ]; const entities = { 2: snapshots[1], 1: snapshots[0] }; @@ -80,10 +81,10 @@ describe('Snapshot Reducer', () => { const state = fromSnapshots.listReducer( { ...initialListState, + entities, ids: ['2', '1'], - entities: entities }, - action + action, ); expect(state.ids).toEqual(['2']); @@ -104,8 +105,9 @@ describe('Snapshot Reducer', () => { state: SnapshotStates.BackedUp, revertable: true, snapshottype: SnapshotType.Daily, - account: 'develop' - }, { + account: 'develop', + }, + { description: 'test snapshot #2', id: '1', created: '2016-01-11T15:59:42+0700', @@ -117,8 +119,9 @@ describe('Snapshot Reducer', () => { state: SnapshotStates.BackedUp, revertable: true, snapshottype: SnapshotType.Daily, - account: 'develop' - }, { + account: 'develop', + }, + { description: 'test snapshot #3', id: '2', created: '2017-10-15T15:59:42+0700', @@ -130,54 +133,42 @@ describe('Snapshot Reducer', () => { state: SnapshotStates.BackedUp, revertable: false, snapshottype: SnapshotType.Manual, - account: 'test' - } + account: 'test', + }, ]; - let slice = fromSnapshots.selectFilteredSnapshots.projector( - differentSnapshots, - { - mode: SnapshotPageMode.Volume, - selectedDate: null, - selectedAccounts: [], - selectedTypes: [] - } - ); + let slice = fromSnapshots.selectFilteredSnapshots.projector(differentSnapshots, { + mode: SnapshotPageMode.Volume, + selectedDate: null, + selectedAccounts: [], + selectedTypes: [], + }); expect(slice).toEqual([differentSnapshots[1], differentSnapshots[2]]); - slice = fromSnapshots.selectFilteredSnapshots.projector( - differentSnapshots, - { - mode: SnapshotPageMode.Volume, - selectedDate: '2017-10-15T00:00:00.000Z', - selectedAccounts: [], - selectedTypes: [] - } - ); + slice = fromSnapshots.selectFilteredSnapshots.projector(differentSnapshots, { + mode: SnapshotPageMode.Volume, + selectedDate: '2017-10-15T00:00:00.000Z', + selectedAccounts: [], + selectedTypes: [], + }); expect(slice).toEqual([differentSnapshots[2]]); - slice = fromSnapshots.selectFilteredSnapshots.projector( - differentSnapshots, - { - mode: SnapshotPageMode.Volume, - selectedDate: null, - selectedAccounts: [], - selectedTypes: [SnapshotType.Daily] - } - ); + slice = fromSnapshots.selectFilteredSnapshots.projector(differentSnapshots, { + mode: SnapshotPageMode.Volume, + selectedDate: null, + selectedAccounts: [], + selectedTypes: [SnapshotType.Daily], + }); expect(slice).toEqual([differentSnapshots[1]]); - slice = fromSnapshots.selectFilteredSnapshots.projector( - differentSnapshots, - { - mode: SnapshotPageMode.Volume, - selectedDate: null, - selectedTypes: [], - selectedAccounts: ['develop'] - } - ); + slice = fromSnapshots.selectFilteredSnapshots.projector(differentSnapshots, { + mode: SnapshotPageMode.Volume, + selectedDate: null, + selectedTypes: [], + selectedAccounts: ['develop'], + }); expect(slice).toEqual([differentSnapshots[1]]); }); diff --git a/src/app/reducers/snapshots/redux/snapshot.reducers.ts b/src/app/reducers/snapshots/redux/snapshot.reducers.ts index 9a8484d70e..105c22bf2f 100644 --- a/src/app/reducers/snapshots/redux/snapshot.reducers.ts +++ b/src/app/reducers/snapshots/redux/snapshot.reducers.ts @@ -1,26 +1,31 @@ import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; import { createFeatureSelector, createSelector } from '@ngrx/store'; -import { getSnapshotDescription, Snapshot, SnapshotPageMode, SnapshotType } from '../../../shared/models'; +import { + getSnapshotDescription, + Snapshot, + SnapshotPageMode, + SnapshotType, +} from '../../../shared/models'; import * as snapshotActions from './snapshot.actions'; import * as volumeActions from '../../volumes/redux/volumes.actions'; import * as moment from 'moment'; export interface State { - list: ListState + list: ListState; } export interface ListState extends EntityState { - loading: boolean, + loading: boolean; filters: { - mode: SnapshotPageMode, - selectedAccounts: string[], - selectedTypes: SnapshotType[], - selectedDate: Date, - selectedGroupings: any[], - query: string - } - selectedSnapshotId: string | null + mode: SnapshotPageMode; + selectedAccounts: string[]; + selectedTypes: SnapshotType[]; + selectedDate: Date; + selectedGroupings: any[]; + query: string; + }; + selectedSnapshotId: string | null; } const sortByCreation = (snapshot1: Snapshot, snapshot2: Snapshot) => { @@ -31,7 +36,7 @@ const sortByCreation = (snapshot1: Snapshot, snapshot2: Snapshot) => { export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: Snapshot) => item.id, - sortComparer: sortByCreation + sortComparer: sortByCreation, }); export const initialListState: ListState = adapter.getInitialState({ @@ -42,10 +47,10 @@ export const initialListState: ListState = adapter.getInitialState({ selectedTypes: [], selectedDate: moment().toDate(), selectedGroupings: [], - query: '' + query: '', }, snapshotIdsByVolumeId: {}, - selectedSnapshotId: '' + selectedSnapshotId: '', }); export interface SnapshotState { @@ -53,13 +58,12 @@ export interface SnapshotState { } export const snapshotReducers = { - list: listReducer + list: listReducer, }; - export function listReducer( state = initialListState, - action: snapshotActions.Actions | volumeActions.Actions + action: snapshotActions.Actions | volumeActions.Actions, ): ListState { switch (action.type) { case snapshotActions.LOAD_SNAPSHOT_REQUEST: { @@ -72,7 +76,7 @@ export function listReducer( case snapshotActions.LOAD_SNAPSHOT_RESPONSE: { const newState = { ...state, - loading: false + loading: false, }; return adapter.addAll([...action.payload], newState); } @@ -84,12 +88,12 @@ export function listReducer( case snapshotActions.LOAD_SELECTED_SNAPSHOT: { return { ...state, - selectedSnapshotId: action.payload + selectedSnapshotId: action.payload, }; } case snapshotActions.ADD_SNAPSHOT_SUCCESS: { - return adapter.upsertOne(action.payload, state) + return adapter.upsertOne(action.payload, state); } case snapshotActions.DELETE_SNAPSHOT_SUCCESS: { @@ -103,95 +107,70 @@ export function listReducer( export const getSnapshotsState = createFeatureSelector('snapshots'); -export const getSnapshotEntitiesState = createSelector( - getSnapshotsState, - state => state.list -); - -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getSnapshotEntitiesState); +export const getSnapshotEntitiesState = createSelector(getSnapshotsState, state => state.list); -export const isLoading = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getSnapshotEntitiesState, - state => state.loading ); -export const getSelectedSnapshot = createSelector( - getSnapshotEntitiesState, - state => state.entities[state.selectedSnapshotId] -); +export const isLoading = createSelector(getSnapshotEntitiesState, state => state.loading); -export const filters = createSelector( +export const getSelectedSnapshot = createSelector( getSnapshotEntitiesState, - state => state.filters -); - -export const viewMode = createSelector( - filters, - state => state.mode -); -export const filterSelectedAccounts = createSelector( - filters, - state => state.selectedAccounts + state => state.entities[state.selectedSnapshotId], ); -export const filterSelectedTypes = createSelector( - filters, - state => state.selectedTypes -); - -export const filterSelectedDate = createSelector( - filters, - state => state.selectedDate -); - -export const filterSelectedGroupings = createSelector( - filters, - state => state.selectedGroupings -); - -export const filterQuery = createSelector( - filters, - state => state.query -); - -export const selectFilteredSnapshots = createSelector( - selectAll, - filters, - (snapshots, filter) => { - const filterByViewMode = (snapshot: Snapshot) => - (filter.mode === SnapshotPageMode.Volume && !!snapshot.volumeid) - || (filter.mode === SnapshotPageMode.VM && !!snapshot.virtualmachineid); - - const filterByTypes = (snapshot: Snapshot) => !filter.selectedTypes.length - || !!filter.selectedTypes.find(type => type === snapshot.snapshottype); - - const filterByAccount = (snapshot: Snapshot) => !filter.selectedAccounts.length - || !!filter.selectedAccounts.find(id => id === snapshot.account); - - const filterByDate = (snapshot: Snapshot) => !filter.selectedDate - || moment(snapshot.created).isBetween( - moment(filter.selectedDate).startOf('day'), - moment(filter.selectedDate).endOf('day') - ); - - const queryLower = filter.query && filter.query.toLowerCase(); - const filterByQuery = (snapshot: Snapshot) => { - return !filter.query - || snapshot.name.toLowerCase().indexOf(queryLower) > -1 - || getSnapshotDescription(snapshot) - && getSnapshotDescription(snapshot).toLowerCase().indexOf(queryLower) > -1; - }; - - return snapshots.filter((snapshot: Snapshot) => - filterByViewMode(snapshot) - && filterByAccount(snapshot) - && filterByTypes(snapshot) - && filterByDate(snapshot) - && filterByQuery(snapshot)); - } -); +export const filters = createSelector(getSnapshotEntitiesState, state => state.filters); + +export const viewMode = createSelector(filters, state => state.mode); +export const filterSelectedAccounts = createSelector(filters, state => state.selectedAccounts); + +export const filterSelectedTypes = createSelector(filters, state => state.selectedTypes); + +export const filterSelectedDate = createSelector(filters, state => state.selectedDate); + +export const filterSelectedGroupings = createSelector(filters, state => state.selectedGroupings); + +export const filterQuery = createSelector(filters, state => state.query); + +export const selectFilteredSnapshots = createSelector(selectAll, filters, (snapshots, filter) => { + const filterByViewMode = (snapshot: Snapshot) => + (filter.mode === SnapshotPageMode.Volume && !!snapshot.volumeid) || + (filter.mode === SnapshotPageMode.VM && !!snapshot.virtualmachineid); + + const filterByTypes = (snapshot: Snapshot) => + !filter.selectedTypes.length || + !!filter.selectedTypes.find(type => type === snapshot.snapshottype); + + const filterByAccount = (snapshot: Snapshot) => + !filter.selectedAccounts.length || + !!filter.selectedAccounts.find(id => id === snapshot.account); + + const filterByDate = (snapshot: Snapshot) => + !filter.selectedDate || + moment(snapshot.created).isBetween( + moment(filter.selectedDate).startOf('day'), + moment(filter.selectedDate).endOf('day'), + ); + + const queryLower = filter.query && filter.query.toLowerCase(); + const filterByQuery = (snapshot: Snapshot) => { + return ( + !filter.query || + snapshot.name.toLowerCase().indexOf(queryLower) > -1 || + (getSnapshotDescription(snapshot) && + getSnapshotDescription(snapshot) + .toLowerCase() + .indexOf(queryLower) > -1) + ); + }; + + return snapshots.filter( + (snapshot: Snapshot) => + filterByViewMode(snapshot) && + filterByAccount(snapshot) && + filterByTypes(snapshot) && + filterByDate(snapshot) && + filterByQuery(snapshot), + ); +}); diff --git a/src/app/reducers/ssh-keys/redux/ssh-key.actions.ts b/src/app/reducers/ssh-keys/redux/ssh-key.actions.ts index e7558fb66a..817e885ba4 100644 --- a/src/app/reducers/ssh-keys/redux/ssh-key.actions.ts +++ b/src/app/reducers/ssh-keys/redux/ssh-key.actions.ts @@ -16,73 +16,64 @@ export const SSH_KEY_PAIR_CREATE_ERROR = '[SshKeys] SSH_KEY_PAIR_CREATE_ERROR'; export class LoadSshKeyRequest implements Action { readonly type = LOAD_SSH_KEYS_REQUEST; - constructor(public payload?: any) { - } + constructor(public payload?: any) {} } export class LoadSshKeyResponse implements Action { readonly type = LOAD_SSH_KEYS_RESPONSE; - constructor(public payload: SSHKeyPair[]) { - } + constructor(public payload: SSHKeyPair[]) {} } export class SshKeyFilterUpdate implements Action { readonly type = SSH_KEY_FILTER_UPDATE; - constructor(public payload: { - selectedGroupings?: Grouping[], - selectedAccountIds?: string[] - }) { - } + constructor( + public payload: { + selectedGroupings?: Grouping[]; + selectedAccountIds?: string[]; + }, + ) {} } export class RemoveSshKeyPair implements Action { readonly type = SSH_KEY_PAIR_REMOVE; - constructor(public payload: SSHKeyPair) { - } + constructor(public payload: SSHKeyPair) {} } export class RemoveSshKeyPairSuccessAction implements Action { readonly type = SSH_KEY_PAIR_REMOVE_SUCCESS; - constructor(public payload: SSHKeyPair) { - } + constructor(public payload: SSHKeyPair) {} } - export class RemoveSshKeyPairErrorAction implements Action { readonly type = SSH_KEY_PAIR_REMOVE_ERROR; - constructor(public payload?: Error) { - } + constructor(public payload?: Error) {} } export class CreateSshKeyPair implements Action { readonly type = SSH_KEY_PAIR_CREATE; - constructor(public payload: SshKeyCreationData) { - } + constructor(public payload: SshKeyCreationData) {} } export class CreateSshKeyPairSuccessAction implements Action { readonly type = SSH_KEY_PAIR_CREATE_SUCCESS; - constructor(public payload: SSHKeyPair) { - } + constructor(public payload: SSHKeyPair) {} } export class CreateSshKeyPairErrorAction implements Action { readonly type = SSH_KEY_PAIR_CREATE_ERROR; - constructor(public payload: Error) { - } + constructor(public payload: Error) {} } - export type Actions = - LoadSshKeyRequest + | LoadSshKeyRequest | LoadSshKeyResponse | SshKeyFilterUpdate | RemoveSshKeyPair diff --git a/src/app/reducers/ssh-keys/redux/ssh-key.effects.ts b/src/app/reducers/ssh-keys/redux/ssh-key.effects.ts index d50cf9f706..7b1f6b8eed 100644 --- a/src/app/reducers/ssh-keys/redux/ssh-key.effects.ts +++ b/src/app/reducers/ssh-keys/redux/ssh-key.effects.ts @@ -4,7 +4,15 @@ import { MatDialog } from '@angular/material'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { Action } from '@ngrx/store'; import { Observable, of } from 'rxjs'; -import { catchError, filter, map, mergeMap, onErrorResumeNext, switchMap, tap } from 'rxjs/operators'; +import { + catchError, + filter, + map, + mergeMap, + onErrorResumeNext, + switchMap, + tap, +} from 'rxjs/operators'; import { SSHKeyPairService } from '../../../shared/services/ssh-keypair.service'; import { SnackBarService } from '../../../core/services'; @@ -21,11 +29,12 @@ export class SshKeyEffects { loadSshKeys$: Observable = this.actions$.pipe( ofType(sshKeyActions.LOAD_SSH_KEYS_REQUEST), switchMap((action: sshKeyActions.LoadSshKeyRequest) => { - return this.sshKeyService - .getListAll(action.payload).pipe( - map((sshKeys: SSHKeyPair[]) => new sshKeyActions.LoadSshKeyResponse(sshKeys)), - catchError(() => of(new sshKeyActions.LoadSshKeyResponse([])))); - })); + return this.sshKeyService.getListAll(action.payload).pipe( + map((sshKeys: SSHKeyPair[]) => new sshKeyActions.LoadSshKeyResponse(sshKeys)), + catchError(() => of(new sshKeyActions.LoadSshKeyResponse([]))), + ); + }), + ); @Effect() removeSshKeyPair$: Observable = this.actions$.pipe( @@ -35,44 +44,52 @@ export class SshKeyEffects { onErrorResumeNext(), filter(res => !!res), switchMap(() => { - return this.sshKeyService.remove({ - name: action.payload.name, - account: action.payload.account, - domainid: action.payload.domainid - }).pipe( - tap(() => { - const message = 'NOTIFICATIONS.SSH_KEY.DELETE_DONE'; - this.showNotificationsOnFinish(message); - }), - map(() => new sshKeyActions.RemoveSshKeyPairSuccessAction(action.payload)), - catchError((error: Error) => { - this.showNotificationsOnFail(error); - return of(new sshKeyActions.RemoveSshKeyPairErrorAction(error)); - })); - })); - })); + return this.sshKeyService + .remove({ + name: action.payload.name, + account: action.payload.account, + domainid: action.payload.domainid, + }) + .pipe( + tap(() => { + const message = 'NOTIFICATIONS.SSH_KEY.DELETE_DONE'; + this.showNotificationsOnFinish(message); + }), + map(() => new sshKeyActions.RemoveSshKeyPairSuccessAction(action.payload)), + catchError((error: Error) => { + this.showNotificationsOnFail(error); + return of(new sshKeyActions.RemoveSshKeyPairErrorAction(error)); + }), + ); + }), + ); + }), + ); @Effect({ dispatch: false }) removeSshKeyPairSuccessNavigate$: Observable = this.actions$.pipe( ofType(sshKeyActions.SSH_KEY_PAIR_REMOVE_SUCCESS), map((action: sshKeyActions.RemoveSshKeyPairSuccessAction) => action.payload), filter((sshKey: SSHKeyPair) => { - return this.router.isActive(`/ssh-keys/view/${sshKey.name}`, false) - && this.router.routerState.root.snapshot.queryParams.account === sshKey.account; + return ( + this.router.isActive(`/ssh-keys/view/${sshKey.name}`, false) && + this.router.routerState.root.snapshot.queryParams.account === sshKey.account + ); }), tap(() => { this.router.navigate(['./ssh-keys'], { - queryParamsHandling: 'preserve' + queryParamsHandling: 'preserve', }); - })); + }), + ); @Effect() createSshKeyPair$: Observable = this.actions$.pipe( ofType(sshKeyActions.SSH_KEY_PAIR_CREATE), mergeMap((action: sshKeyActions.CreateSshKeyPair) => { return (action.payload.publicKey - ? this.sshKeyService.register(action.payload) - : this.sshKeyService.create(action.payload) + ? this.sshKeyService.register(action.payload) + : this.sshKeyService.create(action.payload) ).pipe( tap(() => { const message = 'NOTIFICATIONS.SSH_KEY.CREATION_DONE'; @@ -82,8 +99,10 @@ export class SshKeyEffects { catchError((error: Error) => { this.showNotificationsOnFail(error); return of(new sshKeyActions.CreateSshKeyPairErrorAction(error)); - })); - })); + }), + ); + }), + ); @Effect({ dispatch: false }) createSshKeySuccessPair$: Observable = this.actions$.pipe( @@ -94,7 +113,8 @@ export class SshKeyEffects { } else { this.dialog.closeAll(); } - })); + }), + ); constructor( private actions$: Actions, @@ -102,15 +122,15 @@ export class SshKeyEffects { private dialog: MatDialog, private dialogService: DialogService, private router: Router, - private snackBarService: SnackBarService - ) { - } + private snackBarService: SnackBarService, + ) {} private showPrivateKey(privateKey: string): void { - this.dialog.open(SshPrivateKeyDialogComponent, { - data: privateKey, - width: '400px', - }) + this.dialog + .open(SshPrivateKeyDialogComponent, { + data: privateKey, + width: '400px', + }) .beforeClose() .subscribe(() => this.dialog.closeAll()); } @@ -123,9 +143,8 @@ export class SshKeyEffects { this.dialogService.alert({ message: { translationToken: error.message, - interpolateParams: error.params - } + interpolateParams: error.params, + }, }); } - } diff --git a/src/app/reducers/ssh-keys/redux/ssh-key.reducers.ts b/src/app/reducers/ssh-keys/redux/ssh-key.reducers.ts index ad50695139..e7484ac86a 100644 --- a/src/app/reducers/ssh-keys/redux/ssh-key.reducers.ts +++ b/src/app/reducers/ssh-keys/redux/ssh-key.reducers.ts @@ -10,16 +10,16 @@ import * as sshKeyActions from './ssh-key.actions'; import * as fromVMs from '../../vm/redux/vm.reducers'; export interface State { - list: ListState, - form: FormState + list: ListState; + form: FormState; } export interface ListState extends EntityState { - loading: boolean, + loading: boolean; filters: { - selectedGroupings: Grouping[], - selectedAccountIds: string[] - } + selectedGroupings: Grouping[]; + selectedAccountIds: string[]; + }; } export const sshKeyId = (sshKey: SSHKeyPair) => { @@ -28,29 +28,27 @@ export const sshKeyId = (sshKey: SSHKeyPair) => { export const adapter: EntityAdapter = createEntityAdapter({ selectId: sshKeyId, - sortComparer: Utils.sortByName + sortComparer: Utils.sortByName, }); const initialListState: ListState = adapter.getInitialState({ loading: false, filters: { selectedAccountIds: [], - selectedGroupings: [] - } + selectedGroupings: [], + }, }); - export interface FormState { - loading: boolean, - error: object + loading: boolean; + error: object; } const initialFormState: FormState = { loading: false, - error: null + error: null, }; - export interface SshKeysState { list: ListState; form: FormState; @@ -58,10 +56,9 @@ export interface SshKeysState { export const sshKeyReducers = { list: listReducer, - form: formReducer + form: formReducer, }; - export function listReducer(state = initialListState, action: sshKeyActions.Actions): ListState { switch (action.type) { case sshKeyActions.LOAD_SSH_KEYS_REQUEST: { @@ -75,8 +72,8 @@ export function listReducer(state = initialListState, action: sshKeyActions.Acti ...state, filters: { ...state.filters, - ...action.payload - } + ...action.payload, + }, }; } case sshKeyActions.LOAD_SSH_KEYS_RESPONSE: { @@ -89,12 +86,11 @@ export function listReducer(state = initialListState, action: sshKeyActions.Acti * sort each record upon entry into the sorted array. */ ...adapter.addAll([...action.payload], state), - loading: false + loading: false, }; } case sshKeyActions.SSH_KEY_PAIR_CREATE_SUCCESS: { - return { ...adapter.addOne(action.payload, state), }; @@ -120,26 +116,26 @@ export function formReducer(state = initialFormState, action: sshKeyActions.Acti return { ...state, error: null, - loading: true + loading: true, }; } case sshKeyActions.SSH_KEY_PAIR_CREATE_SUCCESS: { return { ...state, - loading: false + loading: false, }; } case sshKeyActions.SSH_KEY_PAIR_REMOVE_ERROR: { return { ...state, - error: action.payload + error: action.payload, }; } case sshKeyActions.SSH_KEY_PAIR_CREATE_ERROR: { return { ...state, loading: false, - error: action.payload + error: action.payload, }; } default: { @@ -150,54 +146,29 @@ export function formReducer(state = initialFormState, action: sshKeyActions.Acti export const getSshKeysState = createFeatureSelector('sshKeys'); -export const getSshKeysEntitiesState = createSelector( - getSshKeysState, - state => state.list -); +export const getSshKeysEntitiesState = createSelector(getSshKeysState, state => state.list); -export const getSshKeysFormState = createSelector( - getSshKeysState, - state => state.form -); - -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getSshKeysEntitiesState); +export const getSshKeysFormState = createSelector(getSshKeysState, state => state.form); -export const filters = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getSshKeysEntitiesState, - state => state.filters ); -export const isLoading = createSelector( - getSshKeysEntitiesState, - state => state.loading -); +export const filters = createSelector(getSshKeysEntitiesState, state => state.filters); -export const isFormLoading = createSelector( - getSshKeysFormState, - state => state.loading -); +export const isLoading = createSelector(getSshKeysEntitiesState, state => state.loading); -export const filterSelectedGroupings = createSelector( - filters, - state => state.selectedGroupings -); +export const isFormLoading = createSelector(getSshKeysFormState, state => state.loading); -export const filterSelectedAccountIds = createSelector( - filters, - state => state.selectedAccountIds -); +export const filterSelectedGroupings = createSelector(filters, state => state.selectedGroupings); + +export const filterSelectedAccountIds = createSelector(filters, state => state.selectedAccountIds); export const selectFilteredSshKeys = createSelector( selectAll, filterSelectedAccountIds, fromAccounts.selectEntities, (sshKeys, selectedAccountIds, accountEntities) => { - const accountDomainMap = selectedAccountIds .filter(id => accountEntities[id]) .reduce((m, id) => { @@ -205,33 +176,27 @@ export const selectFilteredSshKeys = createSelector( return { ...m, [`${acc.name}_${acc.domainid}`]: acc }; }, {}); - const selectedAccountIdsFilter = sshKey => !selectedAccountIds.length || - (accountDomainMap[`${sshKey.account}_${sshKey.domainid}`]); + const selectedAccountIdsFilter = sshKey => + !selectedAccountIds.length || accountDomainMap[`${sshKey.account}_${sshKey.domainid}`]; - return sshKeys.filter(sshKey => selectedAccountIdsFilter(sshKey)); - } + return sshKeys.filter(selectedAccountIdsFilter); + }, ); -export const selectSSHKeys = createSelector( - selectAll, - fromVMs.getSelectedVM, - (sshKeys, vm) => { - - const selectedVMFilter = sshKey => vm && - vm.account === sshKey.account && vm.domainid === sshKey.domainid; +export const selectSSHKeys = createSelector(selectAll, fromVMs.getSelectedVM, (sshKeys, vm) => { + const selectedVMFilter = sshKey => + vm && vm.account === sshKey.account && vm.domainid === sshKey.domainid; - return sshKeys.filter(sshKey => selectedVMFilter(sshKey)); - } -); + return sshKeys.filter(selectedVMFilter); +}); export const selectSshKeysForAccount = createSelector( selectAll, fromAuth.getUserAccount, (sshKeys, account) => { + const filterSshKeysByAccount = (sshKey: SSHKeyPair) => + account && account.name === sshKey.account && account.domainid === sshKey.domainid; - const filterSshKeysByAccount = (sshKey: SSHKeyPair) => account && - account.name === sshKey.account && account.domainid === sshKey.domainid; - - return sshKeys.filter((sshKey: SSHKeyPair) => filterSshKeysByAccount(sshKey)); - } + return sshKeys.filter(filterSshKeysByAccount); + }, ); diff --git a/src/app/reducers/templates/redux/ostype.actions.ts b/src/app/reducers/templates/redux/ostype.actions.ts index 372b97e86a..206fe49314 100644 --- a/src/app/reducers/templates/redux/ostype.actions.ts +++ b/src/app/reducers/templates/redux/ostype.actions.ts @@ -7,16 +7,13 @@ export const LOAD_OS_TYPES_RESPONSE = '[OS_TYPES] LOAD_OS_TYPES_RESPONSE'; export class LoadOsTypesRequest implements Action { type = LOAD_OS_TYPES_REQUEST; - constructor(public payload?: any) { - } - + constructor(public payload?: any) {} } export class LoadOsTypesResponse implements Action { type = LOAD_OS_TYPES_RESPONSE; - constructor(public payload: OsType[]) { - } + constructor(public payload: OsType[]) {} } export type Actions = LoadOsTypesRequest | LoadOsTypesResponse; diff --git a/src/app/reducers/templates/redux/ostype.effects.ts b/src/app/reducers/templates/redux/ostype.effects.ts index d4765dac61..b70286165c 100644 --- a/src/app/reducers/templates/redux/ostype.effects.ts +++ b/src/app/reducers/templates/redux/ostype.effects.ts @@ -9,7 +9,6 @@ import { OsTypeService } from '../../../shared/services/os-type.service'; import * as osTypesActions from './ostype.actions'; - @Injectable() export class OsTypeEffects { @Effect() @@ -18,12 +17,10 @@ export class OsTypeEffects { switchMap((action: osTypesActions.LoadOsTypesRequest) => { return this.osTypesService.getList(action.payload).pipe( map((osTypes: OsType[]) => new osTypesActions.LoadOsTypesResponse(osTypes)), - catchError(() => of(new osTypesActions.LoadOsTypesResponse([])))); - })); + catchError(() => of(new osTypesActions.LoadOsTypesResponse([]))), + ); + }), + ); - constructor( - private actions$: Actions, - private osTypesService: OsTypeService - ) { - } + constructor(private actions$: Actions, private osTypesService: OsTypeService) {} } diff --git a/src/app/reducers/templates/redux/ostype.reducers.ts b/src/app/reducers/templates/redux/ostype.reducers.ts index d5a2e5bf22..4462ac030f 100644 --- a/src/app/reducers/templates/redux/ostype.reducers.ts +++ b/src/app/reducers/templates/redux/ostype.reducers.ts @@ -1,12 +1,5 @@ -import { - createFeatureSelector, - createSelector -} from '@ngrx/store'; -import { - createEntityAdapter, - EntityAdapter, - EntityState -} from '@ngrx/entity'; +import { createFeatureSelector, createSelector } from '@ngrx/store'; +import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; import * as event from './ostype.actions'; import { OsType } from '../../../shared/models/os-type.model'; @@ -19,7 +12,7 @@ import { OsType } from '../../../shared/models/os-type.model'; * any additional interface properties. */ export interface State extends EntityState { - loading: boolean + loading: boolean; } export interface OsTypeState { @@ -40,7 +33,7 @@ export const osTypeReducers = { */ export const adapter: EntityAdapter = createEntityAdapter({ selectId: (item: OsType) => item.id, - sortComparer: false + sortComparer: false, }); /** getInitialState returns the default initial state @@ -48,18 +41,15 @@ export const adapter: EntityAdapter = createEntityAdapter({ * additional properties can also be defined. */ export const initialState: State = adapter.getInitialState({ - loading: false + loading: false, }); -export function reducer( - state = initialState, - action: event.Actions -): State { +export function reducer(state = initialState, action: event.Actions): State { switch (action.type) { case event.LOAD_OS_TYPES_REQUEST: { return { ...state, - loading: true + loading: true, }; } case event.LOAD_OS_TYPES_RESPONSE: { @@ -72,7 +62,7 @@ export function reducer( * sort each record upon entry into the sorted array. */ ...adapter.addAll(action.payload, state), - loading: false + loading: false, }; } default: { @@ -81,22 +71,12 @@ export function reducer( } } - export const getOsTypesState = createFeatureSelector('osTypes'); -export const getOsTypesEntitiesState = createSelector( - getOsTypesState, - state => state.list -); - -export const { - selectIds, - selectEntities, - selectAll, - selectTotal, -} = adapter.getSelectors(getOsTypesEntitiesState); +export const getOsTypesEntitiesState = createSelector(getOsTypesState, state => state.list); -export const isLoading = createSelector( +export const { selectIds, selectEntities, selectAll, selectTotal } = adapter.getSelectors( getOsTypesEntitiesState, - state => state.loading ); + +export const isLoading = createSelector(getOsTypesEntitiesState, state => state.loading); diff --git a/src/app/reducers/templates/redux/template.actions.ts b/src/app/reducers/templates/redux/template.actions.ts index 6a8d3b6055..fa5c5fed71 100644 --- a/src/app/reducers/templates/redux/template.actions.ts +++ b/src/app/reducers/templates/redux/template.actions.ts @@ -23,151 +23,128 @@ export const SET_TEMPLATE_GROUP_ERROR = '[Templates] SET_TEMPLATE_GROUP_ERROR'; export const RESET_TEMPLATE_GROUP = '[Templates] RESET_TEMPLATE_GROUP'; export const RESET_TEMPLATE_GROUP_SUCCESS = '[Templates] RESET_TEMPLATE_GROUP_SUCCESS'; - export class LoadTemplatesRequest implements Action { readonly type = LOAD_TEMPLATE_REQUEST; - constructor(public payload?: any) { - } + constructor(public payload?: any) {} } export class LoadTemplatesResponse implements Action { readonly type = LOAD_TEMPLATE_RESPONSE; - constructor(public payload: BaseTemplateModel[]) { - } + constructor(public payload: BaseTemplateModel[]) {} } export class LoadSelectedTemplate implements Action { readonly type = LOAD_SELECTED_TEMPLATE; - constructor(public payload: string) { - } + constructor(public payload: string) {} } export class TemplatesFilterUpdate implements Action { readonly type = TEMPLATE_FILTER_UPDATE; - constructor(public payload: any) { - } + constructor(public payload: any) {} } export class DialogTemplatesFilterUpdate implements Action { readonly type = DIALOG_TEMPLATE_FILTER_UPDATE; - constructor(public payload: any) { - } + constructor(public payload: any) {} } export class RegisterTemplate implements Action { readonly type = TEMPLATE_REGISTER; - constructor(public payload: any) { - } + constructor(public payload: any) {} } export class RegisterTemplateSuccess implements Action { readonly type = TEMPLATE_REGISTER_SUCCESS; - constructor(public payload: any) { - } + constructor(public payload: any) {} } export class RegisterTemplateError implements Action { readonly type = TEMPLATE_REGISTER_ERROR; - - constructor(public payload: Error) { - } + constructor(public payload: Error) {} } export class CreateTemplate implements Action { readonly type = TEMPLATE_CREATE; - constructor(public payload: any) { - } + constructor(public payload: any) {} } export class CreateTemplateSuccess implements Action { readonly type = TEMPLATE_CREATE_SUCCESS; - constructor(public payload: any) { - } + constructor(public payload: any) {} } export class CreateTemplateError implements Action { readonly type = TEMPLATE_CREATE_ERROR; - - constructor(public payload: Error) { - } + constructor(public payload: Error) {} } export class RemoveTemplate implements Action { readonly type = TEMPLATE_REMOVE; - constructor(public payload: BaseTemplateModel) { - } + constructor(public payload: BaseTemplateModel) {} } export class RemoveTemplateSuccess implements Action { readonly type = TEMPLATE_REMOVE_SUCCESS; - constructor(public payload: BaseTemplateModel) { - } + constructor(public payload: BaseTemplateModel) {} } export class RemoveTemplateError implements Action { readonly type = TEMPLATE_REMOVE_ERROR; - constructor(public payload?: any) { - } + constructor(public payload?: any) {} } export class UpdateTemplate implements Action { readonly type = UPDATE_TEMPLATE; - constructor(public payload: any) { - } + constructor(public payload: any) {} } export class SetTemplateGroup implements Action { readonly type = SET_TEMPLATE_GROUP; - constructor(public payload: { template: BaseTemplateModel, templateGroup: ImageGroup }) { - } + constructor(public payload: { template: BaseTemplateModel; templateGroup: ImageGroup }) {} } export class SetTemplateGroupSuccess implements Action { readonly type = SET_TEMPLATE_GROUP_SUCCESS; - constructor(public payload: BaseTemplateModel) { - } + constructor(public payload: BaseTemplateModel) {} } export class SetTemplateGroupError implements Action { readonly type = SET_TEMPLATE_GROUP_ERROR; - constructor(public payload: any) { - } + constructor(public payload: any) {} } export class ResetTemplateGroup implements Action { readonly type = RESET_TEMPLATE_GROUP; - constructor(public payload: BaseTemplateModel) { - } + constructor(public payload: BaseTemplateModel) {} } export class ResetTemplateGroupSuccess implements Action { readonly type = RESET_TEMPLATE_GROUP_SUCCESS; - constructor(public payload: BaseTemplateModel) { - } + constructor(public payload: BaseTemplateModel) {} } export type Actions = - LoadTemplatesRequest + | LoadTemplatesRequest | LoadTemplatesResponse | LoadSelectedTemplate | TemplatesFilterUpdate @@ -187,5 +164,3 @@ export type Actions = | SetTemplateGroupError | ResetTemplateGroup | ResetTemplateGroupSuccess; - - diff --git a/src/app/reducers/templates/redux/template.effects.ts b/src/app/reducers/templates/redux/template.effects.ts index 37d0302599..6ea3211144 100644 --- a/src/app/reducers/templates/redux/template.effects.ts +++ b/src/app/reducers/templates/redux/template.effects.ts @@ -7,7 +7,10 @@ import { forkJoin, Observable, of } from 'rxjs'; import { catchError, filter, map, mergeMap, switchMap, tap, withLatestFrom } from 'rxjs/operators'; import * as uniqBy from 'lodash/uniqBy'; -import { TemplateFilters, TemplateResourceType } from '../../../template/shared/base-template.service'; +import { + templateFilters, + templateResourceType, +} from '../../../template/shared/base-template.service'; import { AuthService } from '../../../shared/services/auth.service'; import { SnackBarService } from '../../../core/services'; import { configSelectors, State } from '../../../root-store'; @@ -20,7 +23,7 @@ import { IsoService, resourceType, Template, - TemplateService + TemplateService, } from '../../../template/shared'; import { JobsNotificationService } from '../../../shared/services/jobs-notification.service'; import * as templateActions from './template.actions'; @@ -31,45 +34,52 @@ export class TemplateEffects { loadTemplates$: Observable = this.actions$.pipe( ofType(templateActions.LOAD_TEMPLATE_REQUEST), switchMap((action: templateActions.LoadTemplatesRequest) => { - let filters = [ - TemplateFilters.featured, - TemplateFilters.self - ]; + let filters = [templateFilters.featured, templateFilters.self]; if (this.authService.isAdmin()) { - filters = [TemplateFilters.all]; - } else if (action.payload && action.payload.selectedTypes - && action.payload.selectedTypes.length) { + filters = [templateFilters.all]; + } else if ( + action.payload && + action.payload.selectedTypes && + action.payload.selectedTypes.length + ) { filters = action.payload.selectedTypes; } return forkJoin( - this.templateService.getGroupedTemplates