From f437245d3f28237e715eba0c428e4526c10129e9 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 13 Oct 2020 11:43:08 +0100 Subject: [PATCH 01/74] Allow k8s namespaces to be added as favourites --- .../favorites-global-list.component.ts | 25 ++++++- .../recent-entities.component.ts | 6 +- .../kubernetes/kubernetes-entity-generator.ts | 75 +++++++++++++++++-- .../kubernetes-namespace.component.html | 2 +- .../kubernetes-namespace.component.ts | 23 +++++- .../services/kubernetes-namespace.service.ts | 6 +- .../entity-catalog/entity-catalog.types.ts | 5 ++ .../store/src/favorite-config-mapper.ts | 3 + 8 files changed, 131 insertions(+), 14 deletions(-) diff --git a/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.ts b/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.ts index f8313df5a2..005a9eb320 100644 --- a/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.ts +++ b/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.ts @@ -1,7 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { first, map } from 'rxjs/operators'; import { AppState } from '../../../../../store/src/app-state'; import { getFavoriteInfoObservable } from '../../../../../store/src/helpers/store-helpers'; @@ -31,6 +31,29 @@ export class FavoritesGlobalListComponent implements OnInit { ); this.favInfo$ = getFavoriteInfoObservable(this.store); + + this.validate(); + } + + // Validate all of the entities one by one and update if they are no longer valid + validate() { + this.favoriteGroups$.pipe(first()).subscribe(f => { + console.log('Validating favourites'); + console.log(f); + + f.forEach(group => { + console.log(group); + // Maybe we need to check the enpoint via the health check first? + // Check each entity in turn + group.entities.forEach(entity => { + console.log(entity); + + }); + }) + + }); + + } private sortFavoriteGroups(entityGroups: IGroupedFavorites[]) { diff --git a/src/frontend/packages/core/src/shared/components/recent-entities/recent-entities.component.ts b/src/frontend/packages/core/src/shared/components/recent-entities/recent-entities.component.ts index ed5adeaed9..6ba47a632d 100644 --- a/src/frontend/packages/core/src/shared/components/recent-entities/recent-entities.component.ts +++ b/src/frontend/packages/core/src/shared/components/recent-entities/recent-entities.component.ts @@ -23,13 +23,15 @@ class RenderableRecent { const catalogEntity = entityCatalog.getEntity(entity.endpointType, entity.entityType); this.icon = catalogEntity.definition.icon;; this.iconFont = catalogEntity.definition.iconFont; - + if (!entity.endpointId) { + console.error(`Entity ${entity.guid} does not contain a value for endpointId - recent metadata is malformed`); + } if (entity.entityType === endpointEntityType) { this.subText$ = observableOf(entity.prettyType); } else { this.subText$ = this.store.select(endpointEntitiesSelector).pipe( map(endpoints => { - if (Object.keys(endpoints).length > 1) { + if (entity.endpointId && Object.keys(endpoints).length > 1) { return `${entity.prettyType} - ${endpoints[entity.endpointId].name} (${entity.prettyEndpointType})`; } return entity.prettyType; diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index 7b7d77a960..98c00a4930 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -1,4 +1,5 @@ import { Validators } from '@angular/forms'; +import { of } from 'rxjs'; import { BaseEndpointAuth } from '../../../core/src/core/endpoint-auth'; import { @@ -7,12 +8,14 @@ import { StratosCatalogEntity, } from '../../../store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity'; import { + IEntityMetadata, IStratosEntityDefinition, StratosEndpointExtensionDefinition, } from '../../../store/src/entity-catalog/entity-catalog.types'; import { EndpointAuthTypeConfig, EndpointType } from '../../../store/src/extension-types'; +import { FavoritesConfigMapper } from '../../../store/src/favorite-config-mapper'; import { metricEntityType } from '../../../store/src/helpers/stratos-entity-factory'; -import { IFavoriteMetadata } from '../../../store/src/types/user-favorites.types'; +import { IFavoriteMetadata, UserFavorite } from '../../../store/src/types/user-favorites.types'; import { KubernetesAWSAuthFormComponent } from './auth-forms/kubernetes-aws-auth-form/kubernetes-aws-auth-form.component'; import { KubernetesCertsAuthFormComponent, @@ -63,6 +66,13 @@ import { } from './store/kube.types'; import { generateWorkloadsEntities } from './workloads/store/workloads-entity-generator'; + +export interface IKubeResourceFavMetadata extends IFavoriteMetadata { + guid: string; + kubeGuid: string; + name: string; +} + const enum KubeEndpointAuthTypes { CERT_AUTH = 'kube-cert-auth', CONFIG = 'kubeconfig', @@ -137,6 +147,7 @@ export function generateKubernetesEntities(): StratosBaseCatalogEntity[] { kubeAuthTypeMap[KubeEndpointAuthTypes.CONFIG], BaseEndpointAuth.UsernamePassword ], + favoriteFromEntity: getFavoriteFromKubeEntity, renderPriority: 4, subTypes: [ { @@ -261,11 +272,35 @@ function generateNamespacesEntity(endpointDefinition: StratosEndpointExtensionDe const definition: IStratosEntityDefinition = { type: kubernetesNamespacesEntityType, schema: kubernetesEntityFactory(kubernetesNamespacesEntityType), - endpoint: endpointDefinition + endpoint: endpointDefinition, + label: 'Namespace', + icon: 'namespace', + iconFont: 'stratos-icons', }; - kubeEntityCatalog.namespace = new StratosCatalogEntity(definition, { - actionBuilders: kubeNamespaceActionBuilders - }); + kubeEntityCatalog.namespace = new StratosCatalogEntity(definition, { + actionBuilders: kubeNamespaceActionBuilders, + entityBuilder: { + getIsValid: (favourite) => { + console.log('get is Valid for a namespace'); + console.log(favourite); + return of(false) + }, + getMetadata: (namespace: any) => { + return { + endpointId: namespace.kubeGuid, + guid: namespace.metadata.uid, + kubeGuid: namespace.kubeGuid, + // createdAt: moment(app.metadata.created_at).format('LLL'), + name: namespace.metadata.name, + }; + }, + getLink: metadata => `/kubernetes/${metadata.kubeGuid}/namespaces/${metadata.name}y`, + getGuid: metadata => metadata.guid, + getLines: () => ([ +// ['Hello', (meta) => 'World!'] +// ['Created', (meta) => meta.createdAt] + ]) + }, }); return kubeEntityCatalog.namespace; } @@ -315,3 +350,33 @@ function generateMetricEntity(endpointDefinition: StratosEndpointExtensionDefini }; return new StratosCatalogEntity(definition); } + +function getFavoriteFromKubeEntity( + entity, + entityType: string, + favoritesConfigMapper: FavoritesConfigMapper +): UserFavorite { + console.log('Hello'); + console.log(entity); + return favoritesConfigMapper.getFavoriteFromEntity( + entityType, + KUBERNETES_ENDPOINT_TYPE, + entity.kubeGuid, + entity + ); + + + // if (isCfEntity(entity as CfAPIResource)) { + // return favoritesConfigMapper.getFavoriteFromEntity( + // entityType, + // 'cf', + // entity.entity.cfGuid, + // entity + // ); + // } +} + +// function isCfEntity(entity: CfAPIResource) { +// return entity && entity.entity.cfGuid && entity.metadata && entity.metadata.guid; +// } + diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.html b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.html index 8a042faa2a..749c7af66f 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.html +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.html @@ -1,4 +1,4 @@ - +

{{ kubeNamespaceService.namespaceName }}

diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts index 0a7eab7a1e..5bd6b02238 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts @@ -1,14 +1,19 @@ import { Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { filter, map } from 'rxjs/operators'; +import { IAppFavMetadata } from '../../../../cloud-foundry/src/cf-metadata-types'; import { IHeaderBreadcrumb } from '../../../../core/src/shared/components/page-header/page-header.types'; +import { FavoritesConfigMapper } from '../../../../store/src/favorite-config-mapper'; +import { getFavoriteFromEntity } from '../../../../store/src/user-favorite-helpers'; +import { kubernetesNamespacesEntityType } from '../kubernetes-entity-factory'; import { BaseKubeGuid } from '../kubernetes-page.types'; import { KubernetesEndpointService } from '../services/kubernetes-endpoint.service'; import { KubernetesNamespaceService } from '../services/kubernetes-namespace.service'; import { KubernetesAnalysisService } from '../services/kubernetes.analysis.service'; import { KubernetesService } from '../services/kubernetes.service'; +import { KUBERNETES_ENDPOINT_TYPE } from './../kubernetes-entity-factory'; @Component({ selector: 'app-kubernetes-namespace', @@ -42,6 +47,7 @@ export class KubernetesNamespaceComponent { public kubeEndpointService: KubernetesEndpointService, public kubeNamespaceService: KubernetesNamespaceService, public analysisService: KubernetesAnalysisService, + private favoritesConfigMapper: FavoritesConfigMapper, ) { this.breadcrumbs$ = kubeEndpointService.endpoint$.pipe( map(endpoint => ([{ @@ -58,4 +64,19 @@ export class KubernetesNamespaceComponent { { link: 'analysis', label: 'Analysis', icon: 'assignment', hidden$: this.analysisService.hideAnalysis$ }, ]; } + + public favorite$ = this.kubeNamespaceService.namespace$.pipe( + // tap(a => console.log(a)), + filter(app => !!app), + map(namespace => getFavoriteFromEntity( + { + kubeGuid: this.kubeEndpointService.baseKube.guid, + ...namespace, + prettyText: 'Kubernetes Namespace', + }, + kubernetesNamespacesEntityType, + this.favoritesConfigMapper, + KUBERNETES_ENDPOINT_TYPE + )) + ); } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-namespace.service.ts b/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-namespace.service.ts index d6c62cf663..a86b1e2f19 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-namespace.service.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-namespace.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; -import { filter, first, map, publishReplay } from 'rxjs/operators'; +import { filter, map } from 'rxjs/operators'; import { getIdFromRoute } from '../../../../core/src/core/utils.service'; import { kubeEntityCatalog } from '../kubernetes-entity-catalog'; @@ -25,10 +25,8 @@ export class KubernetesNamespaceService { const namespaceEntity = kubeEntityCatalog.namespace.store.getEntityService(this.namespaceName, this.kubeGuid); this.namespace$ = namespaceEntity.entityObs$.pipe( - filter(p => !!p), + filter(p => !!p && !!p.entity), map(p => p.entity), - publishReplay(1), - first() ); } diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts index e82fff15c7..0f8b33f81c 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts @@ -218,6 +218,11 @@ export interface IStratosEntityBuilder { singular: string, plural: string, }; + /** + * Checks if the given entity is stil valid (e.g. has not been deleted) + * @param entityMetadata Entity metadata + */ + getIsValid?(entityMetadata: T): Observable; /** * Actions that don't effect an individual entity i.e. create new * @returns global actions diff --git a/src/frontend/packages/store/src/favorite-config-mapper.ts b/src/frontend/packages/store/src/favorite-config-mapper.ts index 8fa3cd6890..f92a71dc95 100644 --- a/src/frontend/packages/store/src/favorite-config-mapper.ts +++ b/src/frontend/packages/store/src/favorite-config-mapper.ts @@ -139,6 +139,9 @@ export class FavoritesConfigMapper { const entityType = isEndpoint ? EntityCatalogHelpers.endpointType : entityDefinition.type; const metadata = catalogEntity.builders.entityBuilder.getMetadata(entity); const guid = isEndpoint ? null : catalogEntity.builders.entityBuilder.getGuid(metadata); + if (!endpointId) { + console.error('User favourite - buildFavoriteFromCatalogEntity - endpointId is undefined'); + } return new UserFavorite( endpointId, endpointType, From 24f0c86cee3f54c513dfbcae57428b377b379a78 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 3 Nov 2020 08:28:13 +0000 Subject: [PATCH 02/74] Improve the home page --- .../cloud-foundry/src/cf-entity-generator.ts | 38 ++++ .../applications/applications.routing.ts | 6 + .../cloud-foundry-endpoint.service.ts | 9 +- ...cloud-foundry-space-summary.component.html | 3 +- ...oundry-organization-summary.component.html | 1 + .../cloud-foundry-summary-tab.component.html | 1 + .../cfhome-card/cfhome-card.component.html | 23 +++ .../cfhome-card/cfhome-card.component.scss | 7 + .../cfhome-card/cfhome-card.component.spec.ts | 25 +++ .../home/cfhome-card/cfhome-card.component.ts | 73 ++++++++ .../home/cfhome-card/cfhome-card.module.ts | 32 ++++ .../card-cf-recent-apps.component.html | 25 ++- .../card-cf-recent-apps.component.scss | 14 +- .../card-cf-recent-apps.component.ts | 58 ++++-- .../compact-app-card.component.html | 16 +- .../compact-app-card.component.scss | 57 ++++++ .../compact-app-card.component.ts | 18 +- .../packages/core/sass/_all-theme.scss | 14 +- .../core/src/features/home/home.module.ts | 18 +- .../core/src/features/home/home.types.ts | 36 ++++ .../favorites-meta-card.component.html | 24 +-- .../favorites-meta-card.component.scss | 8 +- .../favorites-meta-card.component.spec.ts | 0 .../favorites-meta-card.component.theme.scss | 0 .../favorites-meta-card.component.ts | 25 +-- .../home/home/home-page-card.directive.ts | 54 ++++++ .../home-page-endpoint-card.component.html | 47 +++++ .../home-page-endpoint-card.component.scss | 168 ++++++++++++++++++ .../home-page-endpoint-card.component.spec.ts | 25 +++ ...me-page-endpoint-card.component.theme.scss | 18 ++ .../home-page-endpoint-card.component.ts | 127 +++++++++++++ .../home/home/home-page.component.html | 33 ++-- .../home/home/home-page.component.scss | 53 ++++++ .../features/home/home/home-page.component.ts | 149 +++++++++++++--- .../card-number-metric.component.html | 2 +- .../card-number-metric.component.scss | 17 ++ .../card-number-metric.component.theme.scss | 6 + .../card-number-metric.component.ts | 1 + .../favorites-entity-list.component.html | 37 ---- .../favorites-entity-list.component.scss | 42 ----- .../favorites-entity-list.component.spec.ts | 30 ---- .../favorites-entity-list.component.ts | 155 ---------------- .../favorites-global-list.component.html | 23 --- .../favorites-global-list.component.scss | 23 --- .../favorites-global-list.component.spec.ts | 26 --- ...favorites-global-list.component.theme.scss | 8 - .../favorites-global-list.component.ts | 84 --------- .../meta-card-base/meta-card.component.html | 2 +- .../meta-card-base/meta-card.component.scss | 14 ++ .../meta-card.component.theme.scss | 18 ++ .../meta-card-base/meta-card.component.ts | 3 + .../recent-entities.component.html | 5 - .../recent-entities.component.ts | 2 +- .../packages/core/src/shared/shared.module.ts | 8 - .../home/kubernetes-home-card.component.html | 23 +++ .../home/kubernetes-home-card.component.scss | 7 + .../kubernetes-home-card.component.spec.ts | 25 +++ .../home/kubernetes-home-card.component.ts | 57 ++++++ .../home/kubernetes-home-card.module.ts | 30 ++++ .../kubernetes/kubernetes-entity-generator.ts | 48 ++--- .../store/src/actions/dashboard-actions.ts | 9 +- .../entity-catalog/entity-catalog.types.ts | 20 +++ .../store/src/reducers/dashboard-reducer.ts | 17 +- .../store/src/user-favorite-manager.ts | 25 ++- 64 files changed, 1390 insertions(+), 582 deletions(-) create mode 100644 src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html create mode 100644 src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss create mode 100644 src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.spec.ts create mode 100644 src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts create mode 100644 src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.module.ts create mode 100644 src/frontend/packages/core/src/features/home/home.types.ts rename src/frontend/packages/core/src/{shared/components => features/home/home}/favorites-meta-card/favorites-meta-card.component.html (71%) rename src/frontend/packages/core/src/{shared/components => features/home/home}/favorites-meta-card/favorites-meta-card.component.scss (95%) rename src/frontend/packages/core/src/{shared/components => features/home/home}/favorites-meta-card/favorites-meta-card.component.spec.ts (100%) rename src/frontend/packages/core/src/{shared/components => features/home/home}/favorites-meta-card/favorites-meta-card.component.theme.scss (100%) rename src/frontend/packages/core/src/{shared/components => features/home/home}/favorites-meta-card/favorites-meta-card.component.ts (83%) create mode 100644 src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts create mode 100644 src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html create mode 100644 src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss create mode 100644 src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts create mode 100644 src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.theme.scss create mode 100644 src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts delete mode 100644 src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.html delete mode 100644 src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.scss delete mode 100644 src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.spec.ts delete mode 100644 src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.ts delete mode 100644 src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.html delete mode 100644 src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.scss delete mode 100644 src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.spec.ts delete mode 100644 src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.theme.scss delete mode 100644 src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.ts create mode 100644 src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.html create mode 100644 src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.scss create mode 100644 src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.spec.ts create mode 100644 src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts create mode 100644 src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.module.ts diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index 20e226cbd5..e91d2aa5b4 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -1,3 +1,4 @@ +import { Compiler, Injector } from '@angular/core'; import { Action, Store } from '@ngrx/store'; import moment from 'moment'; import { combineLatest, Observable, of } from 'rxjs'; @@ -236,6 +237,33 @@ export interface CFBasePipelineRequestActionMeta { flatten?: boolean; } +function cfShortcuts(id: string) { + return [ + { + title: 'View Organizations', + link: ['/cloud-foundry', id, 'organizations'], + icon: 'organization', + iconFont: 'stratos-icons' + }, + { + title: 'View Applications', + link: ['/cloud-foundry', id, 'organizations'], + icon: 'apps' + }, + { + title: 'Deploy an Application', + link: ['/applications', 'new', id], + icon: 'publish' + }, + { + title: 'View Cloud Foundry Info', + link: ['/cloud-foundry', id], + icon: 'cloud_foundry', + iconFont: 'stratos-icons' + }, + ]; +} + export function generateCFEntities(): StratosBaseCatalogEntity[] { const endpointDefinition: StratosEndpointExtensionDefinition = { urlValidationRegexString: urlValidationExpression, @@ -246,6 +274,16 @@ export function generateCFEntities(): StratosBaseCatalogEntity[] { iconFont: 'stratos-icons', logoUrl: '/core/assets/endpoint-icons/cloudfoundry.png', authTypes: [BaseEndpointAuth.UsernamePassword, BaseEndpointAuth.SSO], + homeCard: { + component: (compiler: Compiler, injector: Injector) => import('./features/home/cfhome-card/cfhome-card.module').then((m) => { + return compiler.compileModuleAndAllComponentsAsync(m.CFHomeCardModule).then(cm => { + const mod = cm.ngModuleFactory.create(injector); + return mod.instance.createHomeCard(mod.componentFactoryResolver); + }); + }), + shortcuts: cfShortcuts, + fullView: false, + }, listDetailsComponent: CfEndpointDetailsComponent, renderPriority: 1, healthCheck: new EndpointHealthCheck(CF_ENDPOINT_TYPE, (endpoint) => cfEntityCatalog.cfInfo.api.get(endpoint.guid)), diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/applications.routing.ts b/src/frontend/packages/cloud-foundry/src/features/applications/applications.routing.ts index 1b0ffd86b4..656da9872d 100644 --- a/src/frontend/packages/cloud-foundry/src/features/applications/applications.routing.ts +++ b/src/frontend/packages/cloud-foundry/src/features/applications/applications.routing.ts @@ -39,6 +39,12 @@ const applicationsRoutes: Routes = [ { path: 'new', component: NewApplicationBaseStepComponent, + pathMatch: 'full' + }, + { + path: 'new/:endpointId', + component: NewApplicationBaseStepComponent, + pathMatch: 'full' }, { path: 'create', diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/services/cloud-foundry-endpoint.service.ts b/src/frontend/packages/cloud-foundry/src/features/cf/services/cloud-foundry-endpoint.service.ts index 2a26f72b91..77b3f78921 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/services/cloud-foundry-endpoint.service.ts +++ b/src/frontend/packages/cloud-foundry/src/features/cf/services/cloud-foundry-endpoint.service.ts @@ -140,7 +140,14 @@ export class CloudFoundryEndpointService { private cfUserService: CfUserService, private pmf: PaginationMonitorFactory, ) { - this.cfGuid = activeRouteCfOrgSpace.cfGuid; + if (this.activeRouteCfOrgSpace) { + this.init(this.activeRouteCfOrgSpace); + } + } + + public init(config: ActiveRouteCfOrgSpace) { + this.activeRouteCfOrgSpace = config; + this.cfGuid = this.activeRouteCfOrgSpace.cfGuid; this.cfEndpointEntityService = stratosEntityCatalog.endpoint.store.getEntityService(this.cfGuid); diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component.html b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component.html index 3f8bab4fa0..355e1f50c8 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component.html +++ b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component.html @@ -75,7 +75,8 @@ + [endpoint]="cfEndpointService.cfGuid" + [loading$]="cfSpaceService.loadingApps$" (refresh)="cfSpaceService.fetchApps()"> diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-summary/cloud-foundry-organization-summary.component.html b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-summary/cloud-foundry-organization-summary.component.html index c0b58c00cd..7f08c97325 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-summary/cloud-foundry-organization-summary.component.html +++ b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-summary/cloud-foundry-organization-summary.component.html @@ -75,6 +75,7 @@ diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-summary-tab/cloud-foundry-summary-tab.component.html b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-summary-tab/cloud-foundry-summary-tab.component.html index 00060a2ab9..a45b8fd8f4 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-summary-tab/cloud-foundry-summary-tab.component.html +++ b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-summary-tab/cloud-foundry-summary-tab.component.html @@ -34,6 +34,7 @@ diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html new file mode 100644 index 0000000000..f206632f8c --- /dev/null +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss new file mode 100644 index 0000000000..6e6b1569ce --- /dev/null +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss @@ -0,0 +1,7 @@ +.cf-home-card { + &__plain-tiles { + margin-left: 0; + margin-right: 1em; + margin-top: 1em; + } +} diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.spec.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.spec.ts new file mode 100644 index 0000000000..2ab0ef1072 --- /dev/null +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CFHomeCardComponent } from './cfhome-card.component'; + +describe('CFHomeCardComponent', () => { + let component: CFHomeCardComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CFHomeCardComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CFHomeCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts new file mode 100644 index 0000000000..b0c6a6d4c0 --- /dev/null +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts @@ -0,0 +1,73 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; + +import { PaginationMonitorFactory } from '../../../../../store/src/monitors/pagination-monitor.factory'; +import { EndpointModel } from '../../../../../store/src/public-api'; +import { CFAppState } from '../../../cf-app-state'; +import { ActiveRouteCfOrgSpace } from '../../cf/cf-page.types'; +import { CloudFoundryEndpointService } from '../../cf/services/cloud-foundry-endpoint.service'; +import { HomePageCardLayout } from './../../../../../core/src/features/home/home.types'; + + +@Component({ + selector: 'app-cfhome-card', + templateUrl: './cfhome-card.component.html', + styleUrls: ['./cfhome-card.component.scss'], + providers: [ + { + provide: ActiveRouteCfOrgSpace, + useValue: null, + }, + CloudFoundryEndpointService + ] +}) +export class CFHomeCardComponent implements OnInit { + + @Input() endpoint: EndpointModel; + + _layout: HomePageCardLayout; + + + get layout(): HomePageCardLayout { + return this._layout; + } + + @Input() set layout(value: HomePageCardLayout) { + if (value) { + this._layout = value; + } + this.updateLayout(); + }; + + recentAppsRows = 10; + + appLink: string; + + routeCount$: Observable; + + constructor( + public cfEndpointService: CloudFoundryEndpointService, + private store: Store, + private pmf: PaginationMonitorFactory, + ) {} + + ngOnInit(): void { + const config = new ActiveRouteCfOrgSpace(); + config.cfGuid = this.endpoint.guid; + this.cfEndpointService.init(config); + this.routeCount$ = CloudFoundryEndpointService.fetchRouteCount(this.store, this.pmf, this.endpoint.guid) + + // TODO: + // appLink = ? + } + + public updateLayout() { + this.recentAppsRows = this.layout.y > 1 ? 5 : 10; + + // Hide recent apps if more than 2 columns + if (this.layout.x > 2) { + this.recentAppsRows = 0; + } + } +} diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.module.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.module.ts new file mode 100644 index 0000000000..5ebd521e04 --- /dev/null +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.module.ts @@ -0,0 +1,32 @@ +import { ComponentFactoryResolver, NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { CoreModule } from '../../../../../core/src/core/core.module'; +import { HomeModule } from '../../../../../core/src/features/home/home.module'; +import { SharedModule } from '../../../../../core/src/public-api'; +import { CloudFoundrySharedModule } from '../../../shared/cf-shared.module'; +import { MDAppModule } from './../../../../../core/src/core/md.module'; +import { CFHomeCardComponent } from './cfhome-card.component'; + +@NgModule({ + imports: [ + CoreModule, + RouterModule, + MDAppModule, + SharedModule, + CloudFoundrySharedModule, + HomeModule, + ], + declarations: [ + CFHomeCardComponent, + ], + exports: [ + CFHomeCardComponent, + ], +}) +export class CFHomeCardModule { + + public createHomeCard(componentFactoryResolver: ComponentFactoryResolver) { + return componentFactoryResolver.resolveComponentFactory(CFHomeCardComponent); + } +} diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.html b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.html index f26820ae69..85d9926e0f 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.html +++ b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.html @@ -1,8 +1,8 @@ - - + + - Recently updated applications - Recently updated applications + @@ -10,8 +10,21 @@ There are no applications.
- +
-
\ No newline at end of file +
+ + + + + Recently updated applications + + +
+ +
+
+
+
\ No newline at end of file diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.scss b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.scss index e6f70f9ab0..e376d37732 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.scss +++ b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.scss @@ -1,9 +1,16 @@ +:host { + display: flex; +} .recent-apps-card { display: flex; flex-direction: column; height: 100%; width: 100%; + &__plain { + box-shadow: none; + } + &__content { display: flex; overflow-y: auto; @@ -14,7 +21,6 @@ flex: 1; height: 40px; justify-content: space-between; - padding-top: 0; mat-card-title { margin-bottom: 0; @@ -33,3 +39,9 @@ } } + +// Remove box shadow in plain mode +.recent-apps-card.mat-card.recent-apps-card__plain { + box-shadow: none; + padding: 0 1em; +} diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.ts b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.ts index 136fc16ee1..75b18ed890 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.ts @@ -1,13 +1,12 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { Observable } from 'rxjs'; -import { filter, first, map, tap } from 'rxjs/operators'; +import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; +import { filter, map, tap } from 'rxjs/operators'; -import { CFAppState } from '../../../../../../cloud-foundry/src/cf-app-state'; +import { PaginationObservables } from '../../../../../../store/src/reducers/pagination-reducer/pagination-reducer.types'; import { APIResource } from '../../../../../../store/src/types/api.types'; import { IApp } from '../../../../cf-api.types'; import { cfEntityCatalog } from '../../../../cf-entity-catalog'; -import { appDataSort, CloudFoundryEndpointService } from '../../../../features/cf/services/cloud-foundry-endpoint.service'; +import { appDataSort } from '../../../../features/cf/services/cloud-foundry-endpoint.service'; const RECENT_ITEMS_COUNT = 10; @@ -22,17 +21,42 @@ export class CardCfRecentAppsComponent implements OnInit { @Input() allApps$: Observable[]>; @Input() loading$: Observable; @Output() refresh = new EventEmitter(); + @Input() endpoint: string; + @Input() mode: string; + @Input() showDate: boolean; + @Input() dateMode: string; - constructor( - private store: Store, - public cfEndpointService: CloudFoundryEndpointService, - ) { } + public canRefresh = false; + + public placeholders: any[]; + + appsPagObs: PaginationObservables>; + + private maxRowsSubject = new BehaviorSubject(RECENT_ITEMS_COUNT); + + @Input() set maxRows(value: number) { + this.maxRowsSubject.next(value); + this.placeholders = new Array(value).fill(null); + } + + constructor() { + this.placeholders = new Array(RECENT_ITEMS_COUNT).fill(null); + } ngOnInit() { - this.recentApps$ = this.allApps$.pipe( - filter(apps => !!apps), - first(), - map(apps => this.restrictApps(apps)), + this.canRefresh = this.refresh.observers.length > 0; + this.appsPagObs = cfEntityCatalog.application.store.getPaginationService(this.endpoint); + if (!this.allApps$) { + this.allApps$ = this.appsPagObs.entities$; + this.loading$ = this.appsPagObs.fetchingEntities$; + } + + this.recentApps$ = combineLatest( + this.allApps$, + this.maxRowsSubject.asObservable() + ).pipe( + filter(([apps]) => !!apps), + map(([apps, maxRows]) => this.restrictApps(apps, maxRows)), tap(apps => this.fetchAppStats(apps)) ); } @@ -40,18 +64,16 @@ export class CardCfRecentAppsComponent implements OnInit { private fetchAppStats(recentApps: APIResource[]) { recentApps.forEach(app => { if (app.entity.state === 'STARTED') { - cfEntityCatalog.appStats.api.getMultiple(app.metadata.guid, this.cfEndpointService.cfGuid); + cfEntityCatalog.appStats.api.getMultiple(app.metadata.guid, this.endpoint); } }); } - private restrictApps(apps: APIResource[]): APIResource[] { + private restrictApps(apps: APIResource[], maxRows = RECENT_ITEMS_COUNT): APIResource[] { if (!apps) { return []; } - return apps.sort(appDataSort).slice(0, RECENT_ITEMS_COUNT); + return apps.sort(appDataSort).slice(0, maxRows); } } - - diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.html b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.html index 994e79e2d9..06a1acd218 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.html +++ b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.html @@ -1,7 +1,17 @@ -
+
-
{{ app.metadata.updated_at | date:'medium' }}
+
{{ app.metadata.updated_at | date:'medium' }}
+ + +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.scss b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.scss index ff8d0d2082..bb7def5219 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.scss +++ b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.scss @@ -19,4 +19,61 @@ flex: 1; padding-left: 12px; } + &__subtle { + font-size: 12px; + opacity: 0.7; + } +} + +$ph-bg: #fff !default; +$ph-color: #ced4da !default; +$ph-border-radius: 2px !default; + +$ph-gutter: 30px !default; +$ph-spacer: 15px !default; + +$ph-avatar-border-radius: 50% !default; + +$ph-animation-duration: .8s !default; + +.ph-item { + height: 14px; + background-color: #d8d8d8; + overflow: hidden; + position: relative; + + &::before { + animation: phAnimation $ph-animation-duration linear infinite; + background: linear-gradient(to right, rgba($ph-bg, 0) 46%, rgba($ph-bg, .35) 50%, rgba($ph-bg, 0) 54%) 50% 50%; + content: ''; + top: 0; + right: 0; + bottom: 0; + left: 50%; + z-index: 1; + width: 500%; + margin-left: -250%; + position: absolute; + } + + &.ph-large { + width: 120px; + } + &.ph-small { + width: 80px; + } + &.ph-icon { + border-radius: 50%; + height: 20px; + width: 20px; + } } + +@keyframes phAnimation { + 0% { + transform: translate3d(-30%, 0, 0); + } + 100% { + transform: translate3d(30%, 0, 0); + } +} \ No newline at end of file diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts index 456ba80949..43244cdc49 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts @@ -20,6 +20,11 @@ export class CompactAppCardComponent implements OnInit { @Input() app; + @Input() endpoint: string; + + @Input() showDate = true; + @Input() dateMode: string; + applicationState$: Observable; appStatus$: Observable; @@ -34,14 +39,23 @@ export class CompactAppCardComponent implements OnInit { ) { } ngOnInit() { + if(this.activeRouteCfOrgSpace) { + this.bcType = this.setBreadcrumbType(this.activeRouteCfOrgSpace); + if (!this.endpoint) { + this.endpoint = this.activeRouteCfOrgSpace.cfGuid; + } + } + + if (!this.app) { + return + } - this.bcType = this.setBreadcrumbType(this.activeRouteCfOrgSpace); const initState = this.appStateService.get(this.app.entity, null); this.applicationState$ = ApplicationService.getApplicationState( this.appStateService, this.app.entity, this.app.metadata.guid, - this.activeRouteCfOrgSpace.cfGuid + this.endpoint ).pipe( startWith(initState) ); diff --git a/src/frontend/packages/core/sass/_all-theme.scss b/src/frontend/packages/core/sass/_all-theme.scss index c929676d41..62b2b96554 100644 --- a/src/frontend/packages/core/sass/_all-theme.scss +++ b/src/frontend/packages/core/sass/_all-theme.scss @@ -52,13 +52,11 @@ @import './mat-desktop'; @import './fonts'; @import './ansi-colors'; -@import '../src/shared/components/favorites-global-list/favorites-global-list.component.theme'; -@import '../src/shared/components/favorites-meta-card/favorites-meta-card.component.theme'; - -@import '../../core/src/features/error-page/error-page/error-page.component.theme'; -@import '../../core/src/features/endpoints/backup-restore/restore-endpoints/restore-endpoints.component.theme'; -@import '../../core/src/features/metrics/metrics/metrics.component.theme'; - +@import '../src/features/home/home/favorites-meta-card/favorites-meta-card.component.theme'; +@import '../src/features/home//home/home-page-endpoint-card/home-page-endpoint-card.component.theme'; +@import '../src/features/error-page/error-page/error-page.component.theme'; +@import '../src/features/endpoints/backup-restore/restore-endpoints/restore-endpoints.component.theme'; +@import '../src/features/metrics/metrics/metrics.component.theme'; // Creates the app theme and applies it to the application // $theme = Angular Material Theme @@ -114,7 +112,6 @@ @include stateful-icon($theme, $app-theme); @include app-simple-usage-chart($theme, $app-theme); @include home-page-theme($theme, $app-theme); - @include favorites-global-list-theme($theme, $app-theme); @include favorites-meta-card-theme($theme, $app-theme); @include page-side-nav-theme($theme, $app-theme); @include entity-summary-title-theme($theme, $app-theme); @@ -127,5 +124,6 @@ @include metrics-component-theme($theme, $app-theme); @include intro-screen-theme($theme, $app-theme); @include app-json-view-theme($theme, $app-theme); + @include home-page-endpoint-card-theme($theme, $app-theme); } diff --git a/src/frontend/packages/core/src/features/home/home.module.ts b/src/frontend/packages/core/src/features/home/home.module.ts index db80b5af85..bac5469d38 100644 --- a/src/frontend/packages/core/src/features/home/home.module.ts +++ b/src/frontend/packages/core/src/features/home/home.module.ts @@ -1,17 +1,29 @@ import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { CoreModule, SharedModule } from '@stratosui/core'; -import { CoreModule } from '../../core/core.module'; -import { SharedModule } from '../../shared/shared.module'; +import { MDAppModule } from './../../core/md.module'; +import { FavoritesMetaCardComponent } from './home/favorites-meta-card/favorites-meta-card.component'; +import { HomePageCardDirective } from './home/home-page-card.directive'; +import { HomePageEndpointCardComponent } from './home/home-page-endpoint-card/home-page-endpoint-card.component'; import { HomePageComponent } from './home/home-page.component'; @NgModule({ imports: [ CoreModule, + RouterModule, + MDAppModule, SharedModule, ], declarations: [ - HomePageComponent + HomePageComponent, + HomePageCardDirective, + HomePageEndpointCardComponent, + FavoritesMetaCardComponent, + ], + exports: [ + FavoritesMetaCardComponent, ] }) export class HomeModule { } diff --git a/src/frontend/packages/core/src/features/home/home.types.ts b/src/frontend/packages/core/src/features/home/home.types.ts new file mode 100644 index 0000000000..bd3b690b10 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home.types.ts @@ -0,0 +1,36 @@ +import { HomeCardShortcut } from '../../../../store/src/entity-catalog/entity-catalog.types'; +import { EndpointModel } from '../../../../store/src/public-api'; + +// Layout for a home page card + +// Defined in terms of how many cards we are trying to fit vertically and horizontally +export class HomePageCardLayout { + + public id: number; + + constructor(public x: number, public y: number, public title?: string) { + this.id = x + y * 1000; + } + + public static fromLayout(layout: string, title?: string): HomePageCardLayout { + const parts = layout.split('-'); + const x = parseInt(parts[0], 10); + let y = 1; + if (parts.length > 1) { + y = parseInt(parts[1], 10); + } + + return new HomePageCardLayout(x, y, title ? title : `${x}-${y} Layout`); + } +} + +export abstract class HomePageEndpointCard { + public layout: HomePageCardLayout; + public endpoint: EndpointModel; +} + +export interface LinkMetadata { + favs: any[], + shortcuts: HomeCardShortcut[] +} + diff --git a/src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.html b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html similarity index 71% rename from src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.html rename to src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html index 3c28d55952..edbec137cb 100644 --- a/src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html @@ -1,34 +1,19 @@ + [entityConfig]="entityConfig" mode="plain">
-
- -
-
+
{{ icon.icon }}
-

-
- {{ name$ | async }} -
Disconnected
-
-

-

+

{{ name$ | async }}

{{ prettyName }}
- - - {{ line[0] }} - {{ value }} - -
Favorite not found
@@ -60,6 +45,3 @@

Disconnected
- - - \ No newline at end of file diff --git a/src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.scss b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss similarity index 95% rename from src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.scss rename to src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss index bbb07aa386..e471a073c1 100644 --- a/src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss @@ -1,10 +1,12 @@ .fav-meta-card { - min-height: 160px; outline: none; &__header-text { line-height: 24px; margin-top: 0; } + &__content { + height: 100%; + } &__type { $type-height: 20px; font-size: 13px; @@ -36,6 +38,7 @@ } &__header { display: flex; + } &__logo-panel { display: flex; @@ -52,6 +55,7 @@ opacity: .7; } &__icon-panel { + align-self: center; display: flex; justify-content: left; width: 32px; @@ -89,5 +93,3 @@ .subtle-text { opacity: .6; } - - diff --git a/src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.spec.ts b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.spec.ts similarity index 100% rename from src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.spec.ts rename to src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.spec.ts diff --git a/src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.theme.scss b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.theme.scss similarity index 100% rename from src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.theme.scss rename to src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.theme.scss diff --git a/src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.ts b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts similarity index 83% rename from src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.ts rename to src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts index 06367a9196..2679f73eec 100644 --- a/src/frontend/packages/core/src/shared/components/favorites-meta-card/favorites-meta-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts @@ -2,17 +2,17 @@ import { Component, Input } from '@angular/core'; import { isObservable, Observable, of as observableOf } from 'rxjs'; import { map } from 'rxjs/operators'; -import { entityCatalog } from '../../../../../store/src/entity-catalog/entity-catalog'; -import { IFavoritesMetaCardConfig } from '../../../../../store/src/favorite-config-mapper'; -import { stratosEntityFactory, userFavouritesEntityType } from '../../../../../store/src/helpers/stratos-entity-factory'; -import { stratosEntityCatalog } from '../../../../../store/src/stratos-entity-catalog'; -import { MenuItem } from '../../../../../store/src/types/menu-item.types'; -import { ComponentEntityMonitorConfig, StratosStatus } from '../../../../../store/src/types/shared.types'; -import { IFavoriteEntity } from '../../../../../store/src/types/user-favorite-manager.types'; -import { IFavoriteMetadata, UserFavorite } from '../../../../../store/src/types/user-favorites.types'; -import { isEndpointConnected } from '../../../features/endpoints/connect.service'; -import { ConfirmationDialogConfig } from '../confirmation-dialog.config'; -import { ConfirmationDialogService } from '../confirmation-dialog.service'; +import { IFavoritesMetaCardConfig } from '../../../../../../store/src/favorite-config-mapper'; +import { userFavouritesEntityType } from '../../../../../../store/src/helpers/stratos-entity-factory'; +import { entityCatalog, stratosEntityFactory } from '../../../../../../store/src/public-api'; +import { stratosEntityCatalog } from '../../../../../../store/src/stratos-entity-catalog'; +import { MenuItem } from '../../../../../../store/src/types/menu-item.types'; +import { ComponentEntityMonitorConfig, StratosStatus } from '../../../../../../store/src/types/shared.types'; +import { IFavoriteEntity } from '../../../../../../store/src/types/user-favorite-manager.types'; +import { IFavoriteMetadata, UserFavorite } from '../../../../../../store/src/types/user-favorites.types'; +import { ConfirmationDialogConfig } from '../../../../shared/components/confirmation-dialog.config'; +import { ConfirmationDialogService } from '../../../../shared/components/confirmation-dialog.service'; +import { isEndpointConnected } from '../../../endpoints/connect.service'; interface FavoriteIconData { hasIcon: boolean; @@ -42,6 +42,9 @@ export class FavoritesMetaCardComponent { @Input() public endpointDisconnected = false; + @Input() + public mode: string; + public config: IFavoritesMetaCardConfig; public status$: Observable; diff --git a/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts b/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts new file mode 100644 index 0000000000..b3573b79f1 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts @@ -0,0 +1,54 @@ +import { Compiler, ComponentRef, Directive, Injector, Input, OnDestroy, OnInit, ViewContainerRef } from '@angular/core'; + +import { EndpointModel, entityCatalog } from '../../../../../store/src/public-api'; +import { HomePageCardLayout, HomePageEndpointCard } from './../home.types'; + +@Directive({ + selector: '[appHomeCard]', +}) +export class HomePageCardDirective implements OnInit, OnDestroy { + + private _layout: HomePageCardLayout; + + @Input() appHomeCard: EndpointModel; + + @Input() set layout(value: HomePageCardLayout) { + this._layout = value; + if (this.ref) { + this.ref.instance.layout = value; + } + }; + + private ref: ComponentRef; + + constructor( + private viewContainerRef: ViewContainerRef, + private compiler: Compiler, + private injector: Injector, + ) { } + + ngOnInit() { + // Dynamically load the component + const endpointEntity = entityCatalog.getEndpoint(this.appHomeCard.cnsi_type, this.appHomeCard.sub_type) + if (endpointEntity.definition.homeCard && endpointEntity.definition.homeCard.component) { + this.load(endpointEntity); + } else { + console.warn(`'No endpoint home card for ${this.appHomeCard.guid}`); + } + } + + ngOnDestroy() { + if (this.ref) { + this.ref.destroy(); + } + } + + async load(endpointEntity: any) { + this.viewContainerRef.clear(); + const component = await endpointEntity.definition.homeCard.component(this.compiler, this.injector); + this.ref = this.viewContainerRef.createComponent(component); + (this.ref.instance as any).endpoint = this.appHomeCard; + (this.ref.instance as any).layout = this._layout; + } + +} diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html new file mode 100644 index 0000000000..d3dfb307d1 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -0,0 +1,47 @@ + + +
+ +
+
+

+
+ {{ endpoint.name }} +
+

+
{{ definition.label }}
+
+
+ +
+
+ + +
+
+ +
+
+
+
Favorites
+
+ +
+
+ No favorites +
+
+
Shortcuts
+
+
+
{{ shortcut.icon }}
+ +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss new file mode 100644 index 0000000000..fd210ac214 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss @@ -0,0 +1,168 @@ +.fav-meta-card { + border-radius: 0; + display: flex; + flex-direction: column; + height: 100%; + outline: none; + padding: 0; + + &__header { + display: flex; + margin: 8px 16px 16px 16px; + } + &__content { + display: flex; + height: 100%; + } + &__header-text { + font-size: 18px; + line-height: 24px; + margin-top: 0; + } + &__header-text-panel { + cursor: pointer; + margin-left: 12px; + width: 100%; + &:focus { + outline: none; + } + } + &__logo-panel { + cursor: pointer; + display: flex; + justify-content: center; + width: 42px; + &:focus { + outline: none; + } + } + &__logo { + height: 42px; + width: auto; + } + &__header-star { + display: flex; + margin-left: 20px; + opacity: 0.7; + } + // Check if these are needed + &__type { + $type-height: 20px; + font-size: 13px; + line-height: $type-height; + margin-top: -($type-height - 2px); + opacity: .6; + } + &__missing { + align-items: center; + display: flex; + flex-direction: column; + height: 100%; + justify-content: space-around; + padding: 10px; + padding-top: 20px; + &-text { + font-size: 20px; + font-weight: bold; + } + &-small-text { + margin-bottom: 20px; + } + } + &__clickable { + cursor: pointer; + } + &__icon { + margin-right: 4px; + opacity: .7; + text-align: center; + } + &__icon-panel { + display: flex; + justify-content: left; + width: 32px; + } + &__panel { + display: flex; + :first-child { + flex: 1; + } + } + &__disconnected { + align-self: center; + border-radius: 2px; + display: flex; + font-size: 14px; + font-weight: normal; + margin-right: 8px; + padding: 2px 4px; + } +} + +.error-details { + display: flex; + flex-direction: column; + padding: 0 20px; + &__value { + padding-bottom: 5px; + } + &__label { + font-size: 10px; + opacity: .6; + } +} + +.subtle-text { + opacity: .6; +} + +.home-card { + display: flex; + flex-direction: row; + height: 100%; + width: 100%; + &__left { + flex: 1; + } + &__right { + flex: 0 0 30%; + padding: 16px; + min-width: 200px; + max-width: 400px; + + mat-card { + box-shadow: none; + } + } + &__fav { + > * { + margin-bottom: 10px; + } + } + &__fav-title { + margin-bottom: 10px; + } + &__fav-card { + display: flex; + flex-direction: column; + margin-bottom: 5px; + } + &__shortcut-title { + margin-bottom: 10px; + margin-top: 20px; + } + &__shortcut { + align-items: center; + display: flex; + margin-left: 12px; + + mat-icon { + opacity: 0.7; + text-align: center; + } + } + &__shortcut-icon { + margin-right: 8px; + width: 24px; + } +} diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts new file mode 100644 index 0000000000..75f01e1ce4 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomePageEndpointCardComponent } from './home-page-endpoint-card.component'; + +describe('HomePageEndpointCardComponent', () => { + let component: HomePageEndpointCardComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ HomePageEndpointCardComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomePageEndpointCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.theme.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.theme.scss new file mode 100644 index 0000000000..6e0b7bfc3e --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.theme.scss @@ -0,0 +1,18 @@ +@mixin home-page-endpoint-card-theme($theme, $app-theme) { + $foreground: map-get($theme, foreground); + + .fav-meta-card { + border: 1px solid mat-color($foreground, divider); + &__content { + border-top: 1px solid mat-color($foreground, divider); + } + } + + .home-card__right { + border-left: 1px solid mat-color($foreground, divider); + + mat-card { + border: 1px solid mat-color($foreground, divider);; + } + } +} diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts new file mode 100644 index 0000000000..22f820be22 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -0,0 +1,127 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; +import { first, map } from 'rxjs/operators'; + +import { + EntityCatalogSchemas, + HomeCardShortcut, + IStratosEndpointDefinition, +} from '../../../../../../store/src/entity-catalog/entity-catalog.types'; +import { FavoritesConfigMapper } from '../../../../../../store/src/favorite-config-mapper'; +import { EndpointModel, entityCatalog } from '../../../../../../store/src/public-api'; +import { UserFavoriteManager } from '../../../../../../store/src/user-favorite-manager'; +import { UserFavoriteEndpoint } from './../../../../../../store/src/types/user-favorites.types'; +import { HomePageCardLayout, LinkMetadata } from './../../home.types'; + +const MAX_FAVS = 5; +const MAX_SHORTCUTS = 5; +const MAX_LINKS = 5; + +@Component({ + selector: 'app-home-page-endpoint-card', + templateUrl: './home-page-endpoint-card.component.html', + styleUrls: ['./home-page-endpoint-card.component.scss'] +}) +export class HomePageEndpointCardComponent implements OnInit { + + @Input() endpoint: EndpointModel; + + _layout: HomePageCardLayout; + + get layout(): HomePageCardLayout { + return this._layout; + } + + @Input() set layout(value: HomePageCardLayout) { + if (value) { + this._layout = value; + } + this.updateLayout(); + }; + + favorites$: Observable; + + shortcuts: HomeCardShortcut[]; + + layout$ = new BehaviorSubject(null); + + links$: Observable; + + entity; StratosCatalogEndpointEntity; + + definition: IStratosEndpointDefinition; + + favorite: UserFavoriteEndpoint; + + public link: string; + + // Should the Home Card use the whole width, or do we show the links panel as well? + fullView = false; + + constructor( + private favoritesConfigMapper: FavoritesConfigMapper, + private userFavoriteManager: UserFavoriteManager, + ) { } + + ngOnInit(): void { + + // TODO: Should this be first = it means when you un-favorite, the card stays on the page + this.favorites$ = this.userFavoriteManager.getFavoritesForEndpoint(this.endpoint.guid).pipe( + first(), + map(f => { + return f.map(item => this.userFavoriteManager.mapToHydrated(item)); + }) + ); + + this.entity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type) + this.definition = this.entity.definition; + this.favorite = this.favoritesConfigMapper.getFavoriteEndpointFromEntity(this.endpoint); + + // Get the list of shortcuts for the endpoint for the given endpoint ID + if (this.definition.homeCard && this.definition.homeCard.shortcuts) { + this.shortcuts = this.definition.homeCard.shortcuts(this.endpoint.guid); + } + + this.fullView = this.definition.homeCard && this.definition.homeCard.fullView; + + const mapper = this.favoritesConfigMapper.getMapperFunction(this.favorite); + if (mapper && this.favorite.metadata) { + const p = mapper(this.favorite.metadata); + if (p) { + this.link = p.routerLink; + } + } + + this.links$ = combineLatest([this.favorites$, this.layout$.asObservable()]).pipe( + map(([favs, layout]) => { + let shortcuts: HomeCardShortcut[] = this.shortcuts || []; + const totalShortcuts = shortcuts.length; + // Based on the layout, adjust the numbers returned + if (layout.y > 1) { + if (favs.length > MAX_FAVS) { + favs = favs.slice(0, MAX_FAVS); + } + if (totalShortcuts > MAX_SHORTCUTS) { + shortcuts = this.shortcuts.slice(0, MAX_SHORTCUTS); + } + // We only want to display 5 things + if (favs.length + totalShortcuts > MAX_LINKS) { + let limit = MAX_LINKS - favs.length; + if (limit === 1) { + limit = 0; + } + shortcuts = this.shortcuts.slice(0, limit); + } + } + return { + favs, + shortcuts + }; + }) + ); + } + + public updateLayout() { + this.layout$.next(this.layout); + } +} diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.html b/src/frontend/packages/core/src/features/home/home/home-page.component.html index 90bd4aad65..25e9555af4 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.html @@ -1,23 +1,26 @@ - +

Home

-
-
-
-
-

Favorites

- + +
-
-
-
-

Recent Activity

+ +
+
+ +
+
-
diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.scss b/src/frontend/packages/core/src/features/home/home/home-page.component.scss index 3e0d7d0cf6..a5908725df 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.scss @@ -36,6 +36,29 @@ $page-padding: 20px; margin-bottom: 12px; } } + &__card { + height: 100%; + &:last-child { + margin-bottom: 20px; + } + } + + // Grid layout + &__list { + display: grid; + flex: 1; + grid-column-gap: 10px; + grid-row-gap: 10px; + grid-auto-rows: min-content; + } + + &__list-2 { + grid-template-columns: repeat(2, 1fr); + } + + &__list-3 { + grid-template-columns: repeat(3, 1fr); + } @include breakpoint(tablet) { flex-direction: row; @@ -47,3 +70,33 @@ $page-padding: 20px; padding: 0 $page-padding $page-padding; } } + +.layout-menu { + display: flex; + + &__label { + flex: 1 + } + + mat-icon.layout-menu__icon { + flex: 0 0 20px; + display: flex; + align-self: center; + margin: 0 0 0 10px; + font-size: 20px; + width: 20px; + height: 20px; + line-height: 20px; + opacity: 0; + } + + mat-icon.layout-menu__icon.layout-menu__tick { + opacity: 1; + } + + &__tick { + font-weight: bold; + } + + +} \ No newline at end of file diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.ts index 94e3e09635..2380c22306 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.ts @@ -1,15 +1,17 @@ import { Component } from '@angular/core'; import { Store } from '@ngrx/store'; -import { Observable } from 'rxjs'; +import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; import { first, map } from 'rxjs/operators'; +import { SetHomeCardLayoutAction } from '../../../../../store/src/actions/dashboard-actions'; import { RouterNav } from '../../../../../store/src/actions/router.actions'; import { AppState, IRequestEntityTypeState } from '../../../../../store/src/app-state'; -import { EntityCatalogHelpers } from '../../../../../store/src/entity-catalog/entity-catalog.helper'; -import { IUserFavoritesGroups } from '../../../../../store/src/types/favorite-groups.types'; -import { UserFavorite } from '../../../../../store/src/types/user-favorites.types'; +import { EndpointModel, entityCatalog } from '../../../../../store/src/public-api'; +import { selectDashboardState } from '../../../../../store/src/selectors/dashboard.selectors'; import { UserFavoriteManager } from '../../../../../store/src/user-favorite-manager'; import { EndpointsService } from '../../../core/endpoints.service'; +import { HomePageCardLayout } from '../home.types'; +import { IUserFavoritesGroups } from './../../../../../store/src/types/favorite-groups.types'; @Component({ selector: 'app-home-page', @@ -20,19 +22,63 @@ export class HomePageComponent { public allEndpointIds$: Observable; public haveRegistered$: Observable; - public showFilterToggle$: Observable; - public showFilters = false; + public endpoints$: Observable; + + public layouts$: Observable; + + private layout = new BehaviorSubject(null); + public layout$: Observable; + + public columns = 1; + + public layoutID = 0; + + private layouts: HomePageCardLayout[]; constructor( endpointsService: EndpointsService, - store: Store, + private store: Store, public userFavoriteManager: UserFavoriteManager ) { + this.layout$ = this.layout.asObservable(); this.allEndpointIds$ = endpointsService.endpoints$.pipe( map(endpoints => Object.values(endpoints).map(endpoint => endpoint.guid)) ); this.haveRegistered$ = endpointsService.haveRegistered$; + // Only show endpoints that have Home Card metadata + this.endpoints$ = combineLatest([endpointsService.endpoints$, userFavoriteManager.getAllFavorites()]).pipe( + map(([endpoints, [favGroups, favs]]) => { + const ordered = this.orderEndpoints(endpoints, favGroups); + return ordered.filter(ep => { + const defn = entityCatalog.getEndpoint(ep.cnsi_type, ep.sub_type); + return !!defn.definition.homeCard; + }) + }) + ); + + // Get all endpoints - need to think about the order - those with favorites should appear first + // Would be good to have a way to order the endpoints in the UI + // Need to persist this somewhere - user config? How to remove un-registered endpoints? + + this.layouts = [ + new HomePageCardLayout(0, 0, 'Automatic'), + new HomePageCardLayout(1, 1, 'Single Column'), + new HomePageCardLayout(1, 2, 'Compact Single Column'), + new HomePageCardLayout(2, 1, 'Two Column'), + new HomePageCardLayout(2, 2, 'Compact Two Column'), + new HomePageCardLayout(3, 2, 'Three Column'), + ]; + this.layouts$ = of(this.layouts); + + this.store.select(selectDashboardState).pipe( + map(dashboardState => dashboardState.homeLayout || 0), + first() + ).subscribe(id => { + const selected = this.layouts.find(hpcl => hpcl.id === id) || this.layouts[0]; + this.onChangeLayout(selected); + }) + // Redirect to /applications if not enabled endpointsService.disablePersistenceFeatures$.pipe( map(off => { @@ -48,22 +94,83 @@ export class HomePageComponent { first() ).subscribe(); - this.showFilterToggle$ = userFavoriteManager.getAllFavorites().pipe( - map(([, favEntities]: [IUserFavoritesGroups, IRequestEntityTypeState]) => { - if (favEntities) { - for (const favEntity of Object.values(favEntities)) { - if (favEntity.entityType !== EntityCatalogHelpers.endpointType) { - return true; - } - } - } - return false; - }) - ); } - public toggleShowFilters() { - this.showFilters = !this.showFilters; + public onChangeLayout(layout: HomePageCardLayout) { + + this.layoutID = layout.id; + + // If the layout is automatic, then adjust based on number of things to show + if (layout.id === 0) { + + // TODO + console.log('Automatic layout'); + layout = new HomePageCardLayout(1, 1, 'Full'); + } + + this.layout.next(layout); + + // Update the grid columns based on the layout + this.columns = layout.x; + + // Persist the state + this.store.dispatch(new SetHomeCardLayoutAction(this.layoutID)); + } + + private orderEndpoints(endpoints: IRequestEntityTypeState, favorites: IUserFavoritesGroups): EndpointModel[] { + const processed = {}; + const result = []; + + Object.keys(favorites).forEach(fav => { + if (!favorites[fav].ethereal) { + const id = this.userFavoriteManager.getEndpointIDFromFavoriteID(fav); + if (!!endpoints[id] && !processed[id]) { + processed[id] = true; + result.push(endpoints[id]); + } + } + }); + + Object.keys(favorites).forEach(fav => { + if (favorites[fav].ethereal) { + const id = this.userFavoriteManager.getEndpointIDFromFavoriteID(fav); + if (!!endpoints[id] && !processed[id]) { + processed[id] = true; + result.push(endpoints[id]); + } + } + }); + + Object.values(endpoints).forEach(ep => { + if (!processed[ep.guid]) { + processed[ep.guid] = true; + result.push(ep); + } + }) + + return result; } + + + // Validate all of the entities one by one and update if they are no longer valid + // validate() { + // this.favoriteGroups$.pipe(first()).subscribe(f => { + // console.log('Validating favourites'); + // console.log(f); + + // f.forEach(group => { + // console.log(group); + // // Maybe we need to check the enpoint via the health check first? + // // Check each entity in turn + // group.entities.forEach(entity => { + // console.log(entity); + + // }); + // }) + + // }); + + + // } } diff --git a/src/frontend/packages/core/src/shared/components/cards/card-number-metric/card-number-metric.component.html b/src/frontend/packages/core/src/shared/components/cards/card-number-metric/card-number-metric.component.html index b0747565c1..8290625d06 100644 --- a/src/frontend/packages/core/src/shared/components/cards/card-number-metric/card-number-metric.component.html +++ b/src/frontend/packages/core/src/shared/components/cards/card-number-metric/card-number-metric.component.html @@ -1,6 +1,6 @@
- + {{ icon }}
void | string; @Output() showAlerts = new EventEmitter(); + @Input() mode: string; @Input('alerts') set alerts(alerts) { diff --git a/src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.html b/src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.html deleted file mode 100644 index 450f368b5f..0000000000 --- a/src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.html +++ /dev/null @@ -1,37 +0,0 @@ -
-
- - - - - - - - - {{favoriteType.prettyName}} - - - -
-
No favorites found for the current filters.
-
- -
- - - -
- -
-
- - - -
-
\ No newline at end of file diff --git a/src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.scss b/src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.scss deleted file mode 100644 index 474191d473..0000000000 --- a/src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.scss +++ /dev/null @@ -1,42 +0,0 @@ -@import '../../../../sass/mixins'; - -.favorite-entity-list { - $bottom-space: 30px; - display: flex; - flex-direction: column; - &__search-form { - padding-left: 5px; - padding-top: 1.25em; - } - &__name-search { - padding-right: 20px; - } - &__cards { - display: grid; - grid-column-gap: 10px; - grid-row-gap: 10px; - grid-template-columns: 1fr; - min-height: 0; - min-width: 0; - @include breakpoint(tablet) { - grid-column-gap: $bottom-space; - grid-row-gap: $bottom-space; - grid-template-columns: repeat(3, 1fr); - } - } - &__card { - min-width: 0; - } - &__expand-button { - height: $bottom-space; - line-height: $bottom-space; - margin: auto; - margin-bottom: -20px; - margin-top: 10px; - width: $bottom-space; - } - &__hide-filters { - height: 0; - visibility: hidden; - } -} diff --git a/src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.spec.ts b/src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.spec.ts deleted file mode 100644 index a6bd579fc2..0000000000 --- a/src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { BaseTestModulesNoShared } from '../../../../test-framework/core-test.helper'; -import { SharedModule } from '../../shared.module'; -import { FavoritesEntityListComponent } from './favorites-entity-list.component'; - -describe('FavoritesEntityListComponent', () => { - let component: FavoritesEntityListComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ - ...BaseTestModulesNoShared, - SharedModule - ], - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(FavoritesEntityListComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.ts b/src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.ts deleted file mode 100644 index ff7bccbc37..0000000000 --- a/src/frontend/packages/core/src/shared/components/favorites-entity-list/favorites-entity-list.component.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { Component, Input, OnInit } from '@angular/core'; -import { combineLatest, Observable, ReplaySubject, Subject } from 'rxjs'; -import { distinctUntilChanged, map, scan, startWith } from 'rxjs/operators'; - -import { FavoritesConfigMapper, IFavoriteTypes } from '../../../../../store/src/favorite-config-mapper'; -import { IFavoriteEntity } from '../../../../../store/src/types/user-favorite-manager.types'; - - -@Component({ - selector: 'app-favorites-entity-list', - templateUrl: './favorites-entity-list.component.html', - styleUrls: ['./favorites-entity-list.component.scss'] -}) -export class FavoritesEntityListComponent implements OnInit { - - constructor(private favoritesConfigMapper: FavoritesConfigMapper) {} - - @Input() - set entities(favoriteEntities: IFavoriteEntity[]) { - this.pEntities = favoriteEntities ? [...favoriteEntities] : favoriteEntities; - this.entitiesSubject.next(favoriteEntities); - this.hasEntities = this.pEntities && this.pEntities.length > 0; - } - @Input() - public placeholder = false; - - @Input() - public showFilters = false; - - @Input() - public endpointDisconnected = false; - - @Input() - public autoExpand = false; - - @Input() - set endpointTypes(types: string[] | string) { - if (!this.favoriteTypes) { - if (Array.isArray(types)) { - this.favoriteTypes = types.reduce((allTypes, endpointType) => { - return [ - ...allTypes, - ...this.favoritesConfigMapper.getAllTypesForEndpoint(endpointType) - ]; - }, []); - } else { - this.favoriteTypes = this.favoritesConfigMapper.getAllTypesForEndpoint(types) || []; - } - } - } - - private searchValueSubject = new Subject(); - public set searchValue(searchValue: string) { - this.searchValueSubject.next(searchValue); - } - - public hasEntities = false; - public typeSubject = new ReplaySubject(); - private entitiesSubject = new ReplaySubject(); - private limitToggleSubject = new ReplaySubject(); - - private entities$ = this.entitiesSubject.asObservable(); - public limitedEntities$: Observable; - public searchedEntities$: Observable; - - public noResultsDueToFilter$: Observable; - - public favoriteTypes: IFavoriteTypes[] = null; - - // User to filter favorites - public filterName: string; - public filterType: string; - - public pEntities: IFavoriteEntity[] = []; - - public limitedEntities: IFavoriteEntity[]; - public minLimit = 3; - public limit = this.minLimit; - - public limitToggle$ = this.limitToggleSubject.asObservable().pipe( - scan((acc) => this.minLimit === acc ? null : this.minLimit, this.minLimit), - startWith(this.minLimit) - ); - - public toggleExpand() { - this.limitToggleSubject.next(this.minLimit ? null : this.minLimit); - } - - public typeChanged(type: string) { - this.typeSubject.next(type); - } - - private limitEntities(entities: IFavoriteEntity[], limit: number) { - if (!entities || limit === null) { - return entities; - } else { - return entities.splice(0, limit); - } - } - - public trackByFavoriteId(index: number, entity: IFavoriteEntity) { - return entity.favorite.guid; - } - - ngOnInit() { - const searchValue$ = this.searchValueSubject.pipe(startWith(''), distinctUntilChanged()); - const type$ = this.typeSubject.asObservable().pipe(startWith(null)); - const typesEntities$ = combineLatest( - this.entities$, - type$ - ).pipe( - map(([entities, type]) => { - if (!type) { - return entities; - } - return entities.filter(entity => entity.favorite.entityType === type); - }) - ); - - this.searchedEntities$ = combineLatest( - typesEntities$, - searchValue$.pipe(startWith('')), - ).pipe( - map(([entities, nameSearch]) => { - if (!nameSearch) { - return entities; - } - const searchableEntities = [...entities]; - return searchableEntities.filter(entity => entity.cardMapper(entity.favorite.metadata).name.search(nameSearch) !== -1); - }), - map(searchEntities => searchEntities || []) - ); - - this.limitedEntities$ = combineLatest( - this.searchedEntities$, - this.limitToggle$ - ).pipe( - map(([entities, limit]) => this.limitEntities([...entities], limit)), - map(limitedEntities => limitedEntities || []) - ); - - this.noResultsDueToFilter$ = combineLatest( - searchValue$, - type$, - this.limitedEntities$, - ).pipe( - map(([nameSearch, type, entities]) => entities.length === 0 && (!!nameSearch || !!type)), - startWith(false) - ); - - if (this.autoExpand) { - this.toggleExpand(); - } - } -} diff --git a/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.html b/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.html deleted file mode 100644 index 394ead2585..0000000000 --- a/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.html +++ /dev/null @@ -1,23 +0,0 @@ -
-
Could not fetch favorites
-
- -
- -
-
- - -
-
-
-
-
\ No newline at end of file diff --git a/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.scss b/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.scss deleted file mode 100644 index b1651f7ee0..0000000000 --- a/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.scss +++ /dev/null @@ -1,23 +0,0 @@ -.favorite-list { - &__group { - margin-bottom: 24px; - } - &__endpoint-card { - margin-bottom: 10px; - min-height: 200px; - } - &__entities { - display: flex; - flex-direction: column; - padding-bottom: 24px; - width: 100%; - } - &__empty-text { - margin-bottom: 20px; - } - &__seperator { - border-bottom: 1px solid rgba(0, 0, 0, .2); - margin-left: 25%; - width: 50%; - } -} diff --git a/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.spec.ts b/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.spec.ts deleted file mode 100644 index b429b9affc..0000000000 --- a/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.spec.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { BaseTestModules } from '../../../../test-framework/core-test.helper'; -import { FavoritesGlobalListComponent } from './favorites-global-list.component'; - -describe('FavoritesGlobalListComponent', () => { - let component: FavoritesGlobalListComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [...BaseTestModules], - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(FavoritesGlobalListComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.theme.scss b/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.theme.scss deleted file mode 100644 index d30d6062db..0000000000 --- a/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.theme.scss +++ /dev/null @@ -1,8 +0,0 @@ -@mixin favorites-global-list-theme($theme, $app-theme) { - $foreground: map-get($theme, foreground); - .favorite-list { - &__seperator { - border-bottom: 1px solid mat-color($foreground, divider); - } - } -} diff --git a/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.ts b/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.ts deleted file mode 100644 index 005a9eb320..0000000000 --- a/src/frontend/packages/core/src/shared/components/favorites-global-list/favorites-global-list.component.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { Component, Input, OnInit } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { Observable } from 'rxjs'; -import { first, map } from 'rxjs/operators'; - -import { AppState } from '../../../../../store/src/app-state'; -import { getFavoriteInfoObservable } from '../../../../../store/src/helpers/store-helpers'; -import { IFavoriteEntity, IGroupedFavorites } from '../../../../../store/src/types/user-favorite-manager.types'; -import { IFavoritesInfo } from '../../../../../store/src/types/user-favorites.types'; -import { UserFavoriteManager } from '../../../../../store/src/user-favorite-manager'; - - -@Component({ - selector: 'app-favorites-global-list', - templateUrl: './favorites-global-list.component.html', - styleUrls: ['./favorites-global-list.component.scss'] -}) -export class FavoritesGlobalListComponent implements OnInit { - public favInfo$: Observable; - public favoriteGroups$: Observable; - constructor( - private store: Store, - private userFavoriteManager: UserFavoriteManager - ) { } - - @Input() showFilters: boolean; - - ngOnInit() { - this.favoriteGroups$ = this.userFavoriteManager.hydrateAllFavorites().pipe( - map(favs => this.sortFavoriteGroups(favs)) - ); - - this.favInfo$ = getFavoriteInfoObservable(this.store); - - this.validate(); - } - - // Validate all of the entities one by one and update if they are no longer valid - validate() { - this.favoriteGroups$.pipe(first()).subscribe(f => { - console.log('Validating favourites'); - console.log(f); - - f.forEach(group => { - console.log(group); - // Maybe we need to check the enpoint via the health check first? - // Check each entity in turn - group.entities.forEach(entity => { - console.log(entity); - - }); - }) - - }); - - - } - - private sortFavoriteGroups(entityGroups: IGroupedFavorites[]) { - if (!entityGroups) { - return entityGroups; - } - return entityGroups.map(group => { - if (group.entities) { - group.entities = group.entities.sort(this.sortFavoriteGroup); - } - return group; - }); - } - - private sortFavoriteGroup(entityA: IFavoriteEntity, entityB: IFavoriteEntity) { - if (entityA.favorite.entityType < entityB.favorite.entityType) { - return -1; - } - if (entityA.favorite.entityType > entityB.favorite.entityType) { - return 1; - } - return 0; - } - - public trackByEndpointId(index: number, group: IGroupedFavorites) { - return group.endpoint.favorite.guid; - } -} diff --git a/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.html b/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.html index 0223537772..e275f7d2b1 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.html +++ b/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.html @@ -1,5 +1,5 @@ + [ngClass]="{'meta-card-pointer': clickAction, 'meta-card__plain': mode === 'plain'}" (click)="clickAction ? clickAction() : null">
Deleting
diff --git a/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.scss b/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.scss index fb7bf82696..6cbe51da8e 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.scss +++ b/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.scss @@ -83,3 +83,17 @@ outline: 0; } } + +.mat-card.meta-card__plain { + border-radius: 0; + box-shadow: none; + padding: 5px 10px; + + .meta-card__header { + padding: 0; + } + + .meta-card__title { + margin: 0; + } +} diff --git a/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.theme.scss b/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.theme.scss index e414c3477a..e93de0d3e5 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.theme.scss +++ b/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.theme.scss @@ -10,4 +10,22 @@ .meta-card__header__popup-separator { background-color: mat-color($foreground, divider); } + + .mat-card.meta-card__plain { + border: 1px solid mat-color($foreground, divider); + + .meta-card__title { + margin: 0; + + h3 { + font-size: 14px; + } + .fav-meta-card__type { + font-size: 12px; + } + } + .meta-card__favorite { + align-self: center; + } + } } diff --git a/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.ts b/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.ts index 039954d7f2..35e79c78c2 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.ts @@ -55,6 +55,9 @@ export class MetaCardComponent implements OnDestroy { @Input() statusBackground = false; + @Input() + mode: string; + @Input() clickAction: () => void = null; diff --git a/src/frontend/packages/core/src/shared/components/recent-entities/recent-entities.component.html b/src/frontend/packages/core/src/shared/components/recent-entities/recent-entities.component.html index 70e96a89cc..ad85e67465 100644 --- a/src/frontend/packages/core/src/shared/components/recent-entities/recent-entities.component.html +++ b/src/frontend/packages/core/src/shared/components/recent-entities/recent-entities.component.html @@ -28,11 +28,6 @@
{{ countedEntity.entity.name }}
-
{{ countedEntity.subText$ | async }}
diff --git a/src/frontend/packages/core/src/shared/components/recent-entities/recent-entities.component.ts b/src/frontend/packages/core/src/shared/components/recent-entities/recent-entities.component.ts index 6ba47a632d..7b007be851 100644 --- a/src/frontend/packages/core/src/shared/components/recent-entities/recent-entities.component.ts +++ b/src/frontend/packages/core/src/shared/components/recent-entities/recent-entities.component.ts @@ -32,7 +32,7 @@ class RenderableRecent { this.subText$ = this.store.select(endpointEntitiesSelector).pipe( map(endpoints => { if (entity.endpointId && Object.keys(endpoints).length > 1) { - return `${entity.prettyType} - ${endpoints[entity.endpointId].name} (${entity.prettyEndpointType})`; + return `${entity.prettyType} - ${endpoints[entity.endpointId].name}`; } return entity.prettyType; }) diff --git a/src/frontend/packages/core/src/shared/shared.module.ts b/src/frontend/packages/core/src/shared/shared.module.ts index cb3c000b5d..6b5989eda5 100644 --- a/src/frontend/packages/core/src/shared/shared.module.ts +++ b/src/frontend/packages/core/src/shared/shared.module.ts @@ -34,9 +34,6 @@ import { EditableDisplayValueComponent } from './components/editable-display-val import { EndpointsMissingComponent } from './components/endpoints-missing/endpoints-missing.component'; import { EntitySummaryTitleComponent } from './components/entity-summary-title/entity-summary-title.component'; import { EnumerateComponent } from './components/enumerate/enumerate.component'; -import { FavoritesEntityListComponent } from './components/favorites-entity-list/favorites-entity-list.component'; -import { FavoritesGlobalListComponent } from './components/favorites-global-list/favorites-global-list.component'; -import { FavoritesMetaCardComponent } from './components/favorites-meta-card/favorites-meta-card.component'; import { FileInputComponent } from './components/file-input/file-input.component'; import { FocusDirective } from './components/focus.directive'; import { IntroScreenComponent } from './components/intro-screen/intro-screen.component'; @@ -195,9 +192,6 @@ import { UserPermissionDirective } from './user-permission.directive'; MetricsParentRangeSelectorComponent, StackedInputActionsComponent, StackedInputActionComponent, - FavoritesGlobalListComponent, - FavoritesMetaCardComponent, - FavoritesEntityListComponent, MultilineTitleComponent, TileSelectorComponent, MarkdownPreviewComponent, @@ -289,8 +283,6 @@ import { UserPermissionDirective } from './user-permission.directive'; MetricsParentRangeSelectorComponent, StackedInputActionsComponent, StackedInputActionComponent, - FavoritesMetaCardComponent, - FavoritesGlobalListComponent, MultilineTitleComponent, PageSubNavComponent, BreadcrumbsComponent, diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.html b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.html new file mode 100644 index 0000000000..640c046528 --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.scss b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.scss new file mode 100644 index 0000000000..18a5b079cb --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.scss @@ -0,0 +1,7 @@ +.k8s-home-card { + &__plain-tiles { + margin-left: 0; + margin-right: 1em; + margin-top: 1em; + } +} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.spec.ts b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.spec.ts new file mode 100644 index 0000000000..4cf6c96f76 --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CFHomeCardComponent } from './kubernetes-home-card.component'; + +describe('CFHomeCardComponent', () => { + let component: CFHomeCardComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ CFHomeCardComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CFHomeCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts new file mode 100644 index 0000000000..85d0299f1e --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts @@ -0,0 +1,57 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { HomePageCardLayout } from '../../../../core/src/features/home/home.types'; +import { EndpointModel } from '../../../../store/src/public-api'; +import { kubeEntityCatalog } from '../kubernetes-entity-catalog'; + +@Component({ + selector: 'app-k8s-home-card', + templateUrl: './kubernetes-home-card.component.html', + styleUrls: ['./kubernetes-home-card.component.scss'], + providers: [ + // { + // provide: ActiveRouteCfOrgSpace, + // useValue: null, + // }, + ] +}) +export class KubernetesHomeCardComponent implements OnInit { + + @Input() endpoint: EndpointModel; + + _layout: HomePageCardLayout; + + get layout(): HomePageCardLayout { + return this._layout; + } + + @Input() set layout(value: HomePageCardLayout) { + if (value) { + this._layout = value; + } + }; + + public podCount$: Observable; + public nodeCount$: Observable; + public namespaceCount$: Observable; + + constructor() {} + + ngOnInit() { + const guid = this.endpoint.guid; + + const podsObs = kubeEntityCatalog.pod.store.getPaginationService(guid); + const pods$ = podsObs.entities$; + const nodesObs = kubeEntityCatalog.node.store.getPaginationService(guid); + const nodes$ = nodesObs.entities$; + const namespacesObs = kubeEntityCatalog.namespace.store.getPaginationService(guid); + const namespaces$ = namespacesObs.entities$; + + this.podCount$ = pods$.pipe(map(entities => entities.length)); + this.nodeCount$ = nodes$.pipe(map(entities => entities.length)); + this.namespaceCount$ = namespaces$.pipe(map(entities => entities.length)); + } + +} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.module.ts b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.module.ts new file mode 100644 index 0000000000..099bdbb362 --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.module.ts @@ -0,0 +1,30 @@ +import { ComponentFactoryResolver, NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { CoreModule } from '../../../../core/src/core/core.module'; +import { HomeModule } from '../../../../core/src/features/home/home.module'; +import { SharedModule } from '../../../../core/src/public-api'; +import { MDAppModule } from './../../../../core/src/core/md.module'; +import { KubernetesHomeCardComponent } from './kubernetes-home-card.component'; + +@NgModule({ + imports: [ + CoreModule, + RouterModule, + MDAppModule, + SharedModule, + HomeModule, + ], + declarations: [ + KubernetesHomeCardComponent, + ], + exports: [ + KubernetesHomeCardComponent, + ], +}) +export class KubernetesHomeCardModule { + + public createHomeCard(componentFactoryResolver: ComponentFactoryResolver) { + return componentFactoryResolver.resolveComponentFactory(KubernetesHomeCardComponent); + } +} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index fc1b806581..7a08576349 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -1,3 +1,4 @@ +import { Compiler, Injector } from '@angular/core'; import { Validators } from '@angular/forms'; import { of } from 'rxjs'; @@ -146,6 +147,23 @@ const kubeAuthTypeMap: { [type: string]: EndpointAuthTypeConfig, } = { } }; +function k8sShortcuts(id: string) { + return [ + { + title: 'View Nodes', + link: ['/kubernetes', id, 'nodes'], + icon: 'node', + iconFont: 'stratos-icons' + }, + { + title: 'View Namespaces', + link: ['/kubernetes', id, 'namespaces'], + icon: 'namespace', + iconFont: 'stratos-icons' + } + ]; +} + export function generateKubernetesEntities(): StratosBaseCatalogEntity[] { const endpointDefinition: StratosEndpointExtensionDefinition = { type: KUBERNETES_ENDPOINT_TYPE, @@ -207,7 +225,16 @@ export function generateKubernetesEntities(): StratosBaseCatalogEntity[] { authTypes: [BaseEndpointAuth.UsernamePassword, kubeAuthTypeMap[KubeEndpointAuthTypes.TOKEN]], logoUrl: '/core/assets/custom/k3s.svg', renderPriority: 6 - }] + }], + homeCard: { + component: (compiler: Compiler, injector: Injector) => import('./home/kubernetes-home-card.module').then((m) => { + return compiler.compileModuleAndAllComponentsAsync(m.KubernetesHomeCardModule).then(cm => { + const mod = cm.ngModuleFactory.create(injector); + return mod.instance.createHomeCard(mod.componentFactoryResolver); + }); + }), + shortcuts: k8sShortcuts + } }; return [ generateEndpointEntity(endpointDefinition), @@ -310,10 +337,6 @@ function generateNamespacesEntity(endpointDefinition: StratosEndpointExtensionDe }, getLink: metadata => `/kubernetes/${metadata.kubeGuid}/namespaces/${metadata.name}y`, getGuid: metadata => metadata.guid, - getLines: () => ([ -// ['Hello', (meta) => 'World!'] -// ['Created', (meta) => meta.createdAt] - ]) }, }); return kubeEntityCatalog.namespace; } @@ -378,19 +401,4 @@ function getFavoriteFromKubeEntity( entity.kubeGuid, entity ); - - - // if (isCfEntity(entity as CfAPIResource)) { - // return favoritesConfigMapper.getFavoriteFromEntity( - // entityType, - // 'cf', - // entity.entity.cfGuid, - // entity - // ); - // } } - -// function isCfEntity(entity: CfAPIResource) { -// return entity && entity.entity.cfGuid && entity.metadata && entity.metadata.guid; -// } - diff --git a/src/frontend/packages/store/src/actions/dashboard-actions.ts b/src/frontend/packages/store/src/actions/dashboard-actions.ts index a2bc55101a..81634f7550 100644 --- a/src/frontend/packages/store/src/actions/dashboard-actions.ts +++ b/src/frontend/packages/store/src/actions/dashboard-actions.ts @@ -13,7 +13,8 @@ export const DISABLE_SIDE_NAV_MOBILE_MODE = '[Dashboard] Disable mobile nav'; export const TIMEOUT_SESSION = '[Dashboard] Timeout Session'; export const ENABLE_POLLING = '[Dashboard] Enable Polling'; export const SET_STRATOS_THEME = '[Dashboard] Set Theme'; -export const GRAVATAR_ENABLED = '[Dashboard] Gravatar ENabled'; +export const GRAVATAR_ENABLED = '[Dashboard] Gravatar Enabled'; +export const HOME_CARD_LAYOUT = '[Dashboard] Home Card Layout'; export const HYDRATE_DASHBOARD_STATE = '[Dashboard] Hydrate dashboard state'; @@ -56,6 +57,12 @@ export class SetGravatarEnabledAction implements Action { constructor(public enableGravatar = true) { } type = GRAVATAR_ENABLED; } + +export class SetHomeCardLayoutAction implements Action { + constructor(public id = 0) { } + type = HOME_CARD_LAYOUT; +} + export class HydrateDashboardStateAction implements Action { constructor(public dashboardState: DashboardState) { } type = HYDRATE_DASHBOARD_STATE; diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts index 0f8b33f81c..6ae0bdfff7 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts @@ -1,3 +1,4 @@ +import { Compiler, Injector } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; @@ -60,6 +61,20 @@ export interface IEntityMetadata { [key: string]: string; } +export interface HomeCardShortcut { + title: string; + link: string[]; + icon: string; + iconFont?: string; +} + +// Metadata for Home Card +export interface HomeCardMetadata { + component?: (compiler: Compiler, injector: Injector) => any; + shortcuts?: (endpointID: string) => HomeCardShortcut[]; + fullView?: boolean; +} + /** * Static information describing a base stratos entity. * @@ -161,6 +176,11 @@ export interface IStratosEndpointDefinition) => IListAction[]; + + /** + * Metadata for the card to show on the Home Page for this endpoint type + */ + readonly homeCard?: HomeCardMetadata; } export interface StratosEndpointExtensionDefinition extends Omit { } diff --git a/src/frontend/packages/store/src/reducers/dashboard-reducer.ts b/src/frontend/packages/store/src/reducers/dashboard-reducer.ts index a1737e8cd9..f6a5c6422f 100644 --- a/src/frontend/packages/store/src/reducers/dashboard-reducer.ts +++ b/src/frontend/packages/store/src/reducers/dashboard-reducer.ts @@ -1,4 +1,3 @@ -import { GRAVATAR_ENABLED, SetGravatarEnabledAction } from './../actions/dashboard-actions'; import { CLOSE_SIDE_NAV, DISABLE_SIDE_NAV_MOBILE_MODE, @@ -14,6 +13,12 @@ import { TIMEOUT_SESSION, TOGGLE_SIDE_NAV, } from '../actions/dashboard-actions'; +import { + GRAVATAR_ENABLED, + HOME_CARD_LAYOUT, + SetGravatarEnabledAction, + SetHomeCardLayoutAction, +} from './../actions/dashboard-actions'; export interface DashboardState { timeoutSession: boolean; @@ -25,6 +30,7 @@ export interface DashboardState { themeKey: string; headerEventMinimized: boolean; gravatarEnabled: boolean; + homeLayout: number; } export const defaultDashboardState: DashboardState = { @@ -37,6 +43,7 @@ export const defaultDashboardState: DashboardState = { themeKey: null, headerEventMinimized: false, gravatarEnabled: false, + homeLayout: 0, }; export function dashboardReducer(state: DashboardState = defaultDashboardState, action): DashboardState { @@ -78,7 +85,13 @@ export function dashboardReducer(state: DashboardState = defaultDashboardState, ...state, gravatarEnabled: gravatarAction.enableGravatar }; - case HYDRATE_DASHBOARD_STATE: + case HOME_CARD_LAYOUT: + const layoutAction = action as SetHomeCardLayoutAction; + return { + ...state, + homeLayout: layoutAction.id + }; + case HYDRATE_DASHBOARD_STATE: const hydrateDashboardStateAction = action as HydrateDashboardStateAction; return { ...state, diff --git a/src/frontend/packages/store/src/user-favorite-manager.ts b/src/frontend/packages/store/src/user-favorite-manager.ts index a1e0d211e6..030ff5dc5b 100644 --- a/src/frontend/packages/store/src/user-favorite-manager.ts +++ b/src/frontend/packages/store/src/user-favorite-manager.ts @@ -106,7 +106,7 @@ export class UserFavoriteManager { }); } - private mapToHydrated = (favorite: UserFavorite): IHydrationResults => { + public mapToHydrated = (favorite: UserFavorite): IHydrationResults => { const catalogEntity = entityCatalog.getEntity(favorite.endpointType, favorite.entityType); return { @@ -130,4 +130,27 @@ export class UserFavoriteManager { public toggleFavorite(favorite: UserFavorite) { stratosEntityCatalog.userFavorite.api.toggle(favorite); } + + // Get all favorites for the given endpoint ID + public getFavoritesForEndpoint(endpointID: string): Observable[]> { + const waitForFavorites$ = this.getWaitForFavoritesObservable(); + const favoriteEntities$ = this.store.select(favoriteEntitiesSelector); + return waitForFavorites$.pipe(switchMap(() => favoriteEntities$)).pipe( + map(favs => { + const result = []; + Object.values(favs).forEach(f => { + if (f.endpointId === endpointID && f.entityId) { + result.push(f); + } + }) + return result; + }) + ) + } + + public getEndpointIDFromFavoriteID(id: string): string { + const p = id.split('-'); + const idParts = p.slice(0, p.length - 2); + return idParts.join('-'); + } } From 1c7c0fe8a71c89b7d1303e1e2f4d6cb8fae58145 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 3 Nov 2020 08:37:53 +0000 Subject: [PATCH 03/74] Fix lint issue --- .../kubernetes/kubernetes-entity-generator.ts | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index 7a08576349..71d8603447 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -318,26 +318,27 @@ function generateNamespacesEntity(endpointDefinition: StratosEndpointExtensionDe icon: 'namespace', iconFont: 'stratos-icons', }; - kubeEntityCatalog.namespace = new StratosCatalogEntity(definition, { - actionBuilders: kubeNamespaceActionBuilders, - entityBuilder: { - getIsValid: (favourite) => { - console.log('get is Valid for a namespace'); - console.log(favourite); - return of(false) - }, - getMetadata: (namespace: any) => { - return { - endpointId: namespace.kubeGuid, - guid: namespace.metadata.uid, - kubeGuid: namespace.kubeGuid, - // createdAt: moment(app.metadata.created_at).format('LLL'), - name: namespace.metadata.name, - }; - }, - getLink: metadata => `/kubernetes/${metadata.kubeGuid}/namespaces/${metadata.name}y`, - getGuid: metadata => metadata.guid, - }, }); + kubeEntityCatalog.namespace = new StratosCatalogEntity( + definition, { + actionBuilders: kubeNamespaceActionBuilders, + entityBuilder: { + getIsValid: (favourite) => { + console.log('get is Valid for a namespace'); + console.log(favourite); + return of(false) + }, + getMetadata: (namespace: any) => { + return { + endpointId: namespace.kubeGuid, + guid: namespace.metadata.uid, + kubeGuid: namespace.kubeGuid, + name: namespace.metadata.name, + }; + }, + getLink: metadata => `/kubernetes/${metadata.kubeGuid}/namespaces/${metadata.name}y`, + getGuid: metadata => metadata.guid, + } + }); return kubeEntityCatalog.namespace; } From 0b04ae4fbe247fa12a7b1ffcfb30320cbe7ce053 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 3 Nov 2020 17:57:55 +0000 Subject: [PATCH 04/74] Tweaks and refactoring --- .../core/src/features/home/home.module.ts | 3 + .../home/home/home-page-card.directive.ts | 2 +- .../home-page-endpoint-card.component.html | 24 +++---- .../home-page-endpoint-card.component.scss | 21 +------ .../home-page-endpoint-card.component.ts | 6 +- .../features/home/home/home-page.component.ts | 62 ++++++++++++------- .../home-shortcuts.component.html | 9 +++ .../home-shortcuts.component.scss | 23 +++++++ .../home-shortcuts.component.spec.ts | 25 ++++++++ .../home-shortcuts.component.ts | 14 +++++ .../home/kubernetes-home-card.component.html | 3 + .../home/kubernetes-home-card.component.scss | 3 + .../home/kubernetes-home-card.component.ts | 51 ++++++++++++++- .../kubernetes/kubernetes-entity-generator.ts | 3 +- 14 files changed, 184 insertions(+), 65 deletions(-) create mode 100644 src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.html create mode 100644 src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.scss create mode 100644 src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.spec.ts create mode 100644 src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.ts diff --git a/src/frontend/packages/core/src/features/home/home.module.ts b/src/frontend/packages/core/src/features/home/home.module.ts index bac5469d38..005e3e587f 100644 --- a/src/frontend/packages/core/src/features/home/home.module.ts +++ b/src/frontend/packages/core/src/features/home/home.module.ts @@ -7,6 +7,7 @@ import { FavoritesMetaCardComponent } from './home/favorites-meta-card/favorites import { HomePageCardDirective } from './home/home-page-card.directive'; import { HomePageEndpointCardComponent } from './home/home-page-endpoint-card/home-page-endpoint-card.component'; import { HomePageComponent } from './home/home-page.component'; +import { HomeShortcutsComponent } from './home/home-shortcuts/home-shortcuts.component'; @NgModule({ @@ -21,9 +22,11 @@ import { HomePageComponent } from './home/home-page.component'; HomePageCardDirective, HomePageEndpointCardComponent, FavoritesMetaCardComponent, + HomeShortcutsComponent, ], exports: [ FavoritesMetaCardComponent, + HomeShortcutsComponent, ] }) export class HomeModule { } diff --git a/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts b/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts index b3573b79f1..2d0d51f1a1 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts @@ -28,7 +28,7 @@ export class HomePageCardDirective implements OnInit, OnDestroy { ) { } ngOnInit() { - // Dynamically load the component + // Dynamically load the component for the Home Card const endpointEntity = entityCatalog.getEndpoint(this.appHomeCard.cnsi_type, this.appHomeCard.sub_type) if (endpointEntity.definition.homeCard && endpointEntity.definition.homeCard.component) { this.load(endpointEntity); diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index d3dfb307d1..502ad1e817 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -23,23 +23,15 @@

-
Favorites
-
- -
-
- No favorites -
-
-
Shortcuts
-
-
-
{{ shortcut.icon }}
- -
+
Favorites
+
+
-
+
+
No favorites
+
+
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss index fd210ac214..396989bcae 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss @@ -139,6 +139,9 @@ margin-bottom: 10px; } } + &__fav-none { + margin-left: 12px; + } &__fav-title { margin-bottom: 10px; } @@ -147,22 +150,4 @@ flex-direction: column; margin-bottom: 5px; } - &__shortcut-title { - margin-bottom: 10px; - margin-top: 20px; - } - &__shortcut { - align-items: center; - display: flex; - margin-left: 12px; - - mat-icon { - opacity: 0.7; - text-align: center; - } - } - &__shortcut-icon { - margin-right: 8px; - width: 24px; - } } diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index 22f820be22..a55c77aea0 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; -import { first, map } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; import { EntityCatalogSchemas, @@ -64,10 +64,8 @@ export class HomePageEndpointCardComponent implements OnInit { ) { } ngOnInit(): void { - - // TODO: Should this be first = it means when you un-favorite, the card stays on the page + // Favorites for this endpoint this.favorites$ = this.userFavoriteManager.getFavoritesForEndpoint(this.endpoint.guid).pipe( - first(), map(f => { return f.map(item => this.userFavoriteManager.mapToHydrated(item)); }) diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.ts index 2380c22306..d19e84bd70 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.ts @@ -36,18 +36,18 @@ export class HomePageComponent { private layouts: HomePageCardLayout[]; constructor( - endpointsService: EndpointsService, + public endpointsService: EndpointsService, private store: Store, public userFavoriteManager: UserFavoriteManager ) { this.layout$ = this.layout.asObservable(); - this.allEndpointIds$ = endpointsService.endpoints$.pipe( + this.allEndpointIds$ = this.endpointsService.endpoints$.pipe( map(endpoints => Object.values(endpoints).map(endpoint => endpoint.guid)) ); - this.haveRegistered$ = endpointsService.haveRegistered$; + this.haveRegistered$ = this.endpointsService.haveRegistered$; // Only show endpoints that have Home Card metadata - this.endpoints$ = combineLatest([endpointsService.endpoints$, userFavoriteManager.getAllFavorites()]).pipe( + this.endpoints$ = combineLatest([this.endpointsService.endpoints$, userFavoriteManager.getAllFavorites()]).pipe( map(([endpoints, [favGroups, favs]]) => { const ordered = this.orderEndpoints(endpoints, favGroups); return ordered.filter(ep => { @@ -97,26 +97,25 @@ export class HomePageComponent { } public onChangeLayout(layout: HomePageCardLayout) { - this.layoutID = layout.id; // If the layout is automatic, then adjust based on number of things to show - if (layout.id === 0) { - - // TODO - console.log('Automatic layout'); - layout = new HomePageCardLayout(1, 1, 'Full'); - } + const lay$ = layout.id === 0 ? this.automaticLayout() : of(layout); + lay$.pipe(first()).subscribe(lo => { + this.layout.next(lo); - this.layout.next(layout); + // Update the grid columns based on the layout + this.columns = lo.x; - // Update the grid columns based on the layout - this.columns = layout.x; - - // Persist the state - this.store.dispatch(new SetHomeCardLayoutAction(this.layoutID)); + // Persist the state + this.store.dispatch(new SetHomeCardLayoutAction(this.layoutID)); + }); } + // Order the endpoint cards - we always show all endpoints, order is: + // 1. Endpoint has been added as a favourite + // 2. Endpoint that has child favourites + // 3. Remaining endpoints private orderEndpoints(endpoints: IRequestEntityTypeState, favorites: IUserFavoritesGroups): EndpointModel[] { const processed = {}; const result = []; @@ -151,26 +150,45 @@ export class HomePageComponent { return result; } + // Automatic layout - select the best layout based on the available endpoints + private automaticLayout(): Observable { + return this.endpointsService.endpoints$.pipe( + map(eps => Object.values(eps)), + map(eps => eps.filter(ep => { + const defn = entityCatalog.getEndpoint(ep.cnsi_type, ep.sub_type); + return !!defn.definition.homeCard; + })), + map(eps => { + switch(eps.length) { + case 1: + return new HomePageCardLayout(1, 1); + case 2: + return new HomePageCardLayout(1, 2); + case 3: + return new HomePageCardLayout(2, 2); + case 4: + return new HomePageCardLayout(2, 2); + default: + return new HomePageCardLayout(3, 2); + } + }) + ); + } // Validate all of the entities one by one and update if they are no longer valid // validate() { // this.favoriteGroups$.pipe(first()).subscribe(f => { // console.log('Validating favourites'); // console.log(f); - // f.forEach(group => { // console.log(group); // // Maybe we need to check the enpoint via the health check first? // // Check each entity in turn // group.entities.forEach(entity => { // console.log(entity); - // }); // }) - // }); - - // } } diff --git a/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.html b/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.html new file mode 100644 index 0000000000..9c87aa5a5a --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.html @@ -0,0 +1,9 @@ +
+
Shortcuts
+
+
+
{{ shortcut.icon }}
+ +
+
+
\ No newline at end of file diff --git a/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.scss b/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.scss new file mode 100644 index 0000000000..8cba704199 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.scss @@ -0,0 +1,23 @@ +.home-shortcut { + &__title { + margin-bottom: 10px; + margin-top: 20px; + } + &__item { + align-items: center; + display: flex; + margin-bottom: 10px; + margin-left: 12px; + + mat-icon { + cursor: pointer; + opacity: 0.7; + outline: none; + text-align: center; + } + } + &__icon { + margin-right: 8px; + width: 24px; + } +} diff --git a/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.spec.ts b/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.spec.ts new file mode 100644 index 0000000000..3495a47019 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HomeShortcutsComponent } from './home-shortcuts.component'; + +describe('HomeShortcutsComponent', () => { + let component: HomeShortcutsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ HomeShortcutsComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeShortcutsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.ts b/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.ts new file mode 100644 index 0000000000..1b665083bd --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.ts @@ -0,0 +1,14 @@ +import { Component, Input } from '@angular/core'; + +import { HomeCardShortcut } from '../../../../../../store/src/entity-catalog/entity-catalog.types'; + +@Component({ + selector: 'app-home-shortcuts', + templateUrl: './home-shortcuts.component.html', + styleUrls: ['./home-shortcuts.component.scss'] +}) +export class HomeShortcutsComponent { + + @Input() shortcuts: HomeCardShortcut[]; + +} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.html b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.html index 640c046528..bebe8b03a3 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.html +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.html @@ -18,6 +18,9 @@ +
+ +
diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.scss b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.scss index 18a5b079cb..31d2803d6c 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.scss +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.scss @@ -4,4 +4,7 @@ margin-right: 1em; margin-top: 1em; } + &__shortcuts { + padding: 0 16px; + } } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts index 85d0299f1e..0c09aae1ed 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts @@ -1,10 +1,12 @@ import { Component, Input, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { first, map } from 'rxjs/operators'; import { HomePageCardLayout } from '../../../../core/src/features/home/home.types'; +import { HomeCardShortcut } from '../../../../store/src/entity-catalog/entity-catalog.types'; import { EndpointModel } from '../../../../store/src/public-api'; import { kubeEntityCatalog } from '../kubernetes-entity-catalog'; +import { KubernetesEndpointService } from '../services/kubernetes-endpoint.service'; @Component({ selector: 'app-k8s-home-card', @@ -33,14 +35,18 @@ export class KubernetesHomeCardComponent implements OnInit { } }; + public shortcuts: HomeCardShortcut[]; + public podCount$: Observable; public nodeCount$: Observable; public namespaceCount$: Observable; - constructor() {} + constructor(private kubeEndpointService: KubernetesEndpointService) { + } ngOnInit() { const guid = this.endpoint.guid; + this.kubeEndpointService.initialize(this.endpoint.guid); const podsObs = kubeEntityCatalog.pod.store.getPaginationService(guid); const pods$ = podsObs.entities$; @@ -52,6 +58,45 @@ export class KubernetesHomeCardComponent implements OnInit { this.podCount$ = pods$.pipe(map(entities => entities.length)); this.nodeCount$ = nodes$.pipe(map(entities => entities.length)); this.namespaceCount$ = namespaces$.pipe(map(entities => entities.length)); - } + this.shortcuts = [ + { + title: 'View Nodes', + link: ['/kubernetes', guid, 'nodes'], + icon: 'node', + iconFont: 'stratos-icons' + }, + { + title: 'View Namespaces', + link: ['/kubernetes', guid, 'namespaces'], + icon: 'namespace', + iconFont: 'stratos-icons' + } + ]; + + this.kubeEndpointService.kubeTerminalEnabled$.pipe(first()).subscribe(hasKubeTerminal => { + if (hasKubeTerminal) { + this.shortcuts.push( + { + title: 'Open Terminal', + link: ['/kubernetes', guid, 'terminal'], + icon: 'terminal', + iconFont: 'stratos-icons' + } + ); + } + }); + + this.kubeEndpointService.kubeDashboardConfigured$.pipe(first()).subscribe(hasKubeDashboard => { + if (hasKubeDashboard) { + this.shortcuts.push( + { + title: 'View Dashboard', + link: ['/kubernetes', guid, 'dashboard'], + icon: 'dashboard' + } + ); + } + }); + } } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index 71d8603447..71c729afba 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -233,7 +233,8 @@ export function generateKubernetesEntities(): StratosBaseCatalogEntity[] { return mod.instance.createHomeCard(mod.componentFactoryResolver); }); }), - shortcuts: k8sShortcuts + fullView: true + // shortcuts: k8sShortcuts } }; return [ From f20a801d709838d14c799befec22c784bb3c8b3c Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Wed, 4 Nov 2020 09:31:15 +0000 Subject: [PATCH 05/74] Add separator to layout menu --- .../home/home/home-page.component.html | 11 +++-- .../home/home/home-page.component.scss | 44 ++----------------- .../home/home/home-page.component.theme.scss | 3 ++ .../features/home/home/home-page.component.ts | 1 + 4 files changed, 14 insertions(+), 45 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.html b/src/frontend/packages/core/src/features/home/home/home-page.component.html index 25e9555af4..108409de42 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.html @@ -3,10 +3,13 @@

Home

- +
+ +
+
-
+
diff --git a/src/frontend/packages/core/src/features/dashboard/dashboard.module.ts b/src/frontend/packages/core/src/features/dashboard/dashboard.module.ts index 6df87a993c..712f6f4b18 100644 --- a/src/frontend/packages/core/src/features/dashboard/dashboard.module.ts +++ b/src/frontend/packages/core/src/features/dashboard/dashboard.module.ts @@ -1,16 +1,18 @@ +import { ScrollingModule } from '@angular/cdk/scrolling'; import { NgModule } from '@angular/core'; import { CoreModule } from '../../core/core.module'; import { SharedModule } from '../../shared/shared.module'; -import { DashboardBaseComponent } from './dashboard-base/dashboard-base.component'; -import { SideNavComponent } from './side-nav/side-nav.component'; import { MetricsModule } from '../metrics/metrics.module'; +import { DashboardBaseComponent } from './dashboard-base/dashboard-base.component'; import { PageSideNavComponent } from './page-side-nav/page-side-nav.component'; +import { SideNavComponent } from './side-nav/side-nav.component'; @NgModule({ imports: [ CoreModule, + ScrollingModule, SharedModule, MetricsModule, ], diff --git a/src/frontend/packages/core/src/features/home/home.types.ts b/src/frontend/packages/core/src/features/home/home.types.ts index bd3b690b10..3a5d9f0119 100644 --- a/src/frontend/packages/core/src/features/home/home.types.ts +++ b/src/frontend/packages/core/src/features/home/home.types.ts @@ -1,3 +1,5 @@ +import { Observable } from 'rxjs'; + import { HomeCardShortcut } from '../../../../store/src/entity-catalog/entity-catalog.types'; import { EndpointModel } from '../../../../store/src/public-api'; @@ -27,6 +29,7 @@ export class HomePageCardLayout { export abstract class HomePageEndpointCard { public layout: HomePageCardLayout; public endpoint: EndpointModel; + public load: () => Observable; } export interface LinkMetadata { diff --git a/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts b/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts index 2d0d51f1a1..aeb2bd0a40 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts @@ -1,4 +1,17 @@ -import { Compiler, ComponentRef, Directive, Injector, Input, OnDestroy, OnInit, ViewContainerRef } from '@angular/core'; +import { + Compiler, + ComponentRef, + Directive, + EventEmitter, + Injector, + Input, + OnDestroy, + OnInit, + Output, + ViewContainerRef, +} from '@angular/core'; +import { of, Subscription } from 'rxjs'; +import { filter, first } from 'rxjs/operators'; import { EndpointModel, entityCatalog } from '../../../../../store/src/public-api'; import { HomePageCardLayout, HomePageEndpointCard } from './../home.types'; @@ -8,6 +21,8 @@ import { HomePageCardLayout, HomePageEndpointCard } from './../home.types'; }) export class HomePageCardDirective implements OnInit, OnDestroy { + private canLoad = false; + private _layout: HomePageCardLayout; @Input() appHomeCard: EndpointModel; @@ -19,8 +34,17 @@ export class HomePageCardDirective implements OnInit, OnDestroy { } }; + @Input() set load(value: boolean) { + this.canLoad = value; + this.loadCard(); + } + + @Output() loaded = new EventEmitter(); + private ref: ComponentRef; + private sub: Subscription; + constructor( private viewContainerRef: ViewContainerRef, private compiler: Compiler, @@ -31,7 +55,7 @@ export class HomePageCardDirective implements OnInit, OnDestroy { // Dynamically load the component for the Home Card const endpointEntity = entityCatalog.getEndpoint(this.appHomeCard.cnsi_type, this.appHomeCard.sub_type) if (endpointEntity.definition.homeCard && endpointEntity.definition.homeCard.component) { - this.load(endpointEntity); + this.createCard(endpointEntity); } else { console.warn(`'No endpoint home card for ${this.appHomeCard.guid}`); } @@ -41,14 +65,26 @@ export class HomePageCardDirective implements OnInit, OnDestroy { if (this.ref) { this.ref.destroy(); } + if (this.sub) { + this.sub.unsubscribe(); + } + } + + // Ask the card to load itself + loadCard() { + if (this.canLoad && this.ref && this.ref.instance && this.ref.instance.load) { + const loadObs = this.ref.instance.load() || of(true); + this.sub = loadObs.pipe(filter(v => v === true), first()).subscribe(() => this.loaded.next(null)); + } } - async load(endpointEntity: any) { + async createCard(endpointEntity: any) { this.viewContainerRef.clear(); const component = await endpointEntity.definition.homeCard.component(this.compiler, this.injector); this.ref = this.viewContainerRef.createComponent(component); (this.ref.instance as any).endpoint = this.appHomeCard; (this.ref.instance as any).layout = this._layout; + this.loadCard(); } } diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index 502ad1e817..d799eff359 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -11,15 +11,15 @@

{{ definition.label }}
+
-
- +
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss index 396989bcae..8ac64dc61d 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss @@ -5,8 +5,9 @@ height: 100%; outline: none; padding: 0; - + &__header { + align-items: center; display: flex; margin: 8px 16px 16px 16px; } diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index a55c77aea0..878b523cbe 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -39,6 +39,8 @@ export class HomePageEndpointCardComponent implements OnInit { this.updateLayout(); }; + @Output() loaded = new EventEmitter(); + favorites$: Observable; shortcuts: HomeCardShortcut[]; @@ -55,13 +57,19 @@ export class HomePageEndpointCardComponent implements OnInit { public link: string; + load$: Observable; + loadSubj = new BehaviorSubject(false); + isLoading = false; + // Should the Home Card use the whole width, or do we show the links panel as well? fullView = false; constructor( private favoritesConfigMapper: FavoritesConfigMapper, private userFavoriteManager: UserFavoriteManager, - ) { } + ) { + this.load$ = this.loadSubj.asObservable(); + } ngOnInit(): void { // Favorites for this endpoint @@ -119,6 +127,16 @@ export class HomePageEndpointCardComponent implements OnInit { ); } + public load() { + this.loadSubj.next(true); + this.isLoading = true; + } + + public cardLoaded() { + this.loaded.next(this); + this.isLoading = false; + } + public updateLayout() { this.layout$.next(this.layout); } diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.html b/src/frontend/packages/core/src/features/home/home/home-page.component.html index 108409de42..2f49e19821 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.html @@ -19,10 +19,9 @@

Home

-
- -
- +
+
+
diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.ts index 6c345ab190..b868e311d9 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.ts @@ -1,6 +1,7 @@ -import { Component } from '@angular/core'; +import { ScrollDispatcher } from '@angular/cdk/scrolling'; +import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core'; import { Store } from '@ngrx/store'; -import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; +import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs'; import { first, map } from 'rxjs/operators'; import { SetHomeCardLayoutAction } from '../../../../../store/src/actions/dashboard-actions'; @@ -10,15 +11,16 @@ import { EndpointModel, entityCatalog } from '../../../../../store/src/public-ap import { selectDashboardState } from '../../../../../store/src/selectors/dashboard.selectors'; import { UserFavoriteManager } from '../../../../../store/src/user-favorite-manager'; import { EndpointsService } from '../../../core/endpoints.service'; -import { HomePageCardLayout } from '../home.types'; import { IUserFavoritesGroups } from './../../../../../store/src/types/favorite-groups.types'; +import { HomePageCardLayout } from './../home.types'; +import { HomePageEndpointCardComponent } from './home-page-endpoint-card/home-page-endpoint-card.component'; @Component({ selector: 'app-home-page', templateUrl: './home-page.component.html', styleUrls: ['./home-page.component.scss'] }) -export class HomePageComponent { +export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { public allEndpointIds$: Observable; public haveRegistered$: Observable; @@ -33,13 +35,32 @@ export class HomePageComponent { public layoutID = 0; - private layouts: HomePageCardLayout[]; + private layouts: HomePageCardLayout[] = [ + new HomePageCardLayout(0, 0, 'Automatic'), + null, + new HomePageCardLayout(1, 1, 'Single Column'), + new HomePageCardLayout(1, 2, 'Compact Single Column'), + new HomePageCardLayout(2, 1, 'Two Column'), + new HomePageCardLayout(2, 2, 'Compact Two Column'), + new HomePageCardLayout(3, 2, 'Three Column'), + ]; + + @ViewChild('endpointsPanel') endpointsPanel; + + @ViewChildren(HomePageEndpointCardComponent) endpointCards: QueryList; + @ViewChildren('endpointCard') endpointElements: QueryList; + + notLoadedCardIndices: number[] = []; + + private sub: Subscription; constructor( public endpointsService: EndpointsService, private store: Store, - public userFavoriteManager: UserFavoriteManager + public userFavoriteManager: UserFavoriteManager, + private scrollDispatcher: ScrollDispatcher, ) { + this.layouts$ = of(this.layouts); this.layout$ = this.layout.asObservable(); this.allEndpointIds$ = this.endpointsService.endpoints$.pipe( map(endpoints => Object.values(endpoints).map(endpoint => endpoint.guid)) @@ -57,26 +78,14 @@ export class HomePageComponent { }) ); - // Get all endpoints - need to think about the order - those with favorites should appear first - // Would be good to have a way to order the endpoints in the UI - // Need to persist this somewhere - user config? How to remove un-registered endpoints? - - this.layouts = [ - new HomePageCardLayout(0, 0, 'Automatic'), - null, - new HomePageCardLayout(1, 1, 'Single Column'), - new HomePageCardLayout(1, 2, 'Compact Single Column'), - new HomePageCardLayout(2, 1, 'Two Column'), - new HomePageCardLayout(2, 2, 'Compact Two Column'), - new HomePageCardLayout(3, 2, 'Three Column'), - ]; - this.layouts$ = of(this.layouts); + // Set an initial layout + this.layout.next(new HomePageCardLayout(1, 1)); this.store.select(selectDashboardState).pipe( map(dashboardState => dashboardState.homeLayout || 0), first() ).subscribe(id => { - const selected = this.layouts.find(hpcl => hpcl.id === id) || this.layouts[0]; + const selected = this.layouts.find(hpcl => hpcl && hpcl.id === id) || this.layouts[0]; this.onChangeLayout(selected); }) @@ -97,7 +106,64 @@ export class HomePageComponent { } + ngOnInit() { + // Listen for scroll events - used to load cards as they come into view + this.sub = this.scrollDispatcher.scrolled().subscribe((x:any) => { + const el = x.elementRef.nativeElement; + this.handleScroll(el.scrollTop); + }); + } + + ngOnDestroy() { + if (this.sub) { + this.sub.unsubscribe(); + } + } + + ngAfterViewInit(): void { + this.endpointElements.changes.subscribe(cards => { + this.notLoadedCardIndices = []; + for(let i=0;i< cards.length; i++) { + this.notLoadedCardIndices.push(i); + } + // Schedule a check for cards that are visible and should be loaded + setTimeout(() => this.handleScroll(), 100); + }); + } + + // This is called after a card has loaded - we call the scroll handler again + // to check if there are more cards that are visible and thus can be loaded + cardLoaded() { + console.log('Card loaded'); + this.handleScroll(); + } + + // User has scrolled - check the remaining cards that have not been loaded + // to see if any are now visible and shoule be loaded + // Only load the first one - after that one has loaded, we'll call this method again + // and check for the next one + handleScroll(scrollTop: number = 0) { + const remaining = []; + let processedCard = false; + for (const index of this.notLoadedCardIndices) { + const cardElement = this.endpointElements.toArray()[index] as ElementRef; + const top = cardElement.nativeElement.offsetTop; + const height = this.endpointsPanel.nativeElement.offsetParent.offsetHeight; + const bottom = scrollTop + height; + if (!processedCard && top >= scrollTop && top <= bottom) { + const card = this.endpointCards.toArray()[index]; + card.load(); + processedCard = true; + } else { + remaining.push(index); + } + }; + this.notLoadedCardIndices = remaining; + } + public onChangeLayout(layout: HomePageCardLayout) { + console.log('onChangeLayout'); + console.log(layout); this.layoutID = layout.id; // If the layout is automatic, then adjust based on number of things to show @@ -110,6 +176,10 @@ export class HomePageComponent { // Persist the state this.store.dispatch(new SetHomeCardLayoutAction(this.layoutID)); + + // Ensure we check again if any cards are now visible + console.log('Handle scroll'); + this.handleScroll(); }); } @@ -162,20 +232,24 @@ export class HomePageComponent { map(eps => { switch(eps.length) { case 1: - return new HomePageCardLayout(1, 1); + return this.getLayout(1, 1); case 2: - return new HomePageCardLayout(1, 2); + return this.getLayout(1, 2); case 3: - return new HomePageCardLayout(2, 2); + return this.getLayout(2, 2); case 4: - return new HomePageCardLayout(2, 2); + return this.getLayout(2, 2); default: - return new HomePageCardLayout(3, 2); + return this.getLayout(3, 2); } }) ); } + private getLayout(x: number, y: number): HomePageCardLayout { + return this.layouts.find(item => item && item.x === x && item.y === y); + } + // Validate all of the entities one by one and update if they are no longer valid // validate() { // this.favoriteGroups$.pipe(first()).subscribe(f => { diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts index 0c09aae1ed..2605c4e104 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts @@ -1,5 +1,5 @@ import { Component, Input, OnInit } from '@angular/core'; -import { Observable } from 'rxjs'; +import { combineLatest, Observable } from 'rxjs'; import { first, map } from 'rxjs/operators'; import { HomePageCardLayout } from '../../../../core/src/features/home/home.types'; @@ -11,13 +11,7 @@ import { KubernetesEndpointService } from '../services/kubernetes-endpoint.servi @Component({ selector: 'app-k8s-home-card', templateUrl: './kubernetes-home-card.component.html', - styleUrls: ['./kubernetes-home-card.component.scss'], - providers: [ - // { - // provide: ActiveRouteCfOrgSpace, - // useValue: null, - // }, - ] + styleUrls: ['./kubernetes-home-card.component.scss'] }) export class KubernetesHomeCardComponent implements OnInit { @@ -48,17 +42,6 @@ export class KubernetesHomeCardComponent implements OnInit { const guid = this.endpoint.guid; this.kubeEndpointService.initialize(this.endpoint.guid); - const podsObs = kubeEntityCatalog.pod.store.getPaginationService(guid); - const pods$ = podsObs.entities$; - const nodesObs = kubeEntityCatalog.node.store.getPaginationService(guid); - const nodes$ = nodesObs.entities$; - const namespacesObs = kubeEntityCatalog.namespace.store.getPaginationService(guid); - const namespaces$ = namespacesObs.entities$; - - this.podCount$ = pods$.pipe(map(entities => entities.length)); - this.nodeCount$ = nodes$.pipe(map(entities => entities.length)); - this.namespaceCount$ = namespaces$.pipe(map(entities => entities.length)); - this.shortcuts = [ { title: 'View Nodes', @@ -74,6 +57,23 @@ export class KubernetesHomeCardComponent implements OnInit { } ]; + } + + + // Card is instructed to load its view by the container, whn it is visible + load(): Observable { + const guid = this.endpoint.guid; + const podsObs = kubeEntityCatalog.pod.store.getPaginationService(guid); + const pods$ = podsObs.entities$; + const nodesObs = kubeEntityCatalog.node.store.getPaginationService(guid); + const nodes$ = nodesObs.entities$; + const namespacesObs = kubeEntityCatalog.namespace.store.getPaginationService(guid); + const namespaces$ = namespacesObs.entities$; + + this.podCount$ = pods$.pipe(map(entities => entities.length)); + this.nodeCount$ = nodes$.pipe(map(entities => entities.length)); + this.namespaceCount$ = namespaces$.pipe(map(entities => entities.length)); + this.kubeEndpointService.kubeTerminalEnabled$.pipe(first()).subscribe(hasKubeTerminal => { if (hasKubeTerminal) { this.shortcuts.push( @@ -98,5 +98,9 @@ export class KubernetesHomeCardComponent implements OnInit { ); } }); + + return combineLatest([this.podCount$, this.nodeCount$, this.namespaceCount$]).pipe( + map(() => true) + ); } } From 19fcdc82e81617a31735d34fcee829c96f8a988c Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 5 Nov 2020 15:57:13 +0000 Subject: [PATCH 07/74] Various tidyups and improvements --- .../cloud-foundry-endpoint.service.ts | 25 +-- .../cfhome-card/cfhome-card.component.html | 4 +- .../home/cfhome-card/cfhome-card.component.ts | 22 +-- .../dashboard-base.component.html | 2 +- .../home-page-endpoint-card.component.html | 2 +- .../home-page-endpoint-card.component.ts | 85 ++++++++-- .../home/home/home-page.component.html | 2 +- .../features/home/home/home-page.component.ts | 159 +++++++++++------- 8 files changed, 199 insertions(+), 102 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/services/cloud-foundry-endpoint.service.ts b/src/frontend/packages/cloud-foundry/src/features/cf/services/cloud-foundry-endpoint.service.ts index 77b3f78921..bba49f0208 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/services/cloud-foundry-endpoint.service.ts +++ b/src/frontend/packages/cloud-foundry/src/features/cf/services/cloud-foundry-endpoint.service.ts @@ -134,6 +134,20 @@ export class CloudFoundryEndpointService { return fetchTotalResults(action, store, pmf); } + public static fetchOrgs(store: Store, pmf: PaginationMonitorFactory, cfGuid: string): + Observable[]> { + const getAllOrgsAction = CloudFoundryEndpointService.createGetAllOrganizations(cfGuid); + return getPaginationObservables>({ + store, + action: getAllOrgsAction, + paginationMonitor: pmf.create( + getAllOrgsAction.paginationKey, + cfEntityFactory(organizationEntityType), + getAllOrgsAction.flattenPagination + ) + }, getAllOrgsAction.flattenPagination).entities$; + } + constructor( public activeRouteCfOrgSpace: ActiveRouteCfOrgSpace, private store: Store, @@ -159,16 +173,7 @@ export class CloudFoundryEndpointService { private constructCoreObservables() { this.endpoint$ = this.cfEndpointEntityService.waitForEntity$; - const getAllOrgsAction = CloudFoundryEndpointService.createGetAllOrganizations(this.cfGuid); - this.orgs$ = getPaginationObservables>({ - store: this.store, - action: getAllOrgsAction, - paginationMonitor: this.pmf.create( - getAllOrgsAction.paginationKey, - cfEntityFactory(organizationEntityType), - getAllOrgsAction.flattenPagination - ) - }, getAllOrgsAction.flattenPagination).entities$; + this.orgs$ = CloudFoundryEndpointService.fetchOrgs(this.store, this.pmf, this.cfGuid); this.info$ = this.cfInfoEntityService.waitForEntity$; diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html index 7bcc7bd8c8..c687bbeb84 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html @@ -6,12 +6,12 @@ value="{{ (appCount$ | async) }}"> - - diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts index 5e20ead320..e42fed95b7 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts @@ -45,9 +45,6 @@ export class CFHomeCardComponent { @Input() set endpoint(value: EndpointModel) { this.guid = value.guid; - const config = new ActiveRouteCfOrgSpace(); - config.cfGuid = this.guid; - this.cfEndpointService.init(config); } guid: string; @@ -66,7 +63,6 @@ export class CFHomeCardComponent { private appStatsToLoad: APIResource[] = []; constructor( - public cfEndpointService: CloudFoundryEndpointService, private store: Store, private pmf: PaginationMonitorFactory, ) {} @@ -74,17 +70,19 @@ export class CFHomeCardComponent { // Card is instructed to load its view by the container, whn it is visible load(): Observable { this.cardLoaded = true; - this.routeCount$ = CloudFoundryEndpointService.fetchRouteCount(this.store, this.pmf, this.guid) - - this.appCount$ = this.cfEndpointService.appsPagObs.totalEntities$; - this.orgCount$ = this.cfEndpointService.orgs$.pipe(map(orgs => orgs.length)); + this.routeCount$ = CloudFoundryEndpointService.fetchRouteCount(this.store, this.pmf, this.guid); + this.appCount$ = CloudFoundryEndpointService.fetchAppCount(this.store, this.pmf, this.guid); + this.orgCount$ = CloudFoundryEndpointService.fetchOrgs(this.store, this.pmf, this.guid).pipe(map(orgs => orgs.length)); this.appLink = () => goToAppWall(this.store, this.guid);; + const appsPagObs = cfEntityCatalog.application.store.getPaginationService(this.guid); + // When the apps are loaded, fetch the app stats - this.cfEndpointService.appsPagObs.entities$.pipe(first()).subscribe(apps => { + appsPagObs.entities$.pipe(first()).subscribe(apps => { this.appStatsToLoad = this.restrictApps(apps); this.fetchAppStats(); + this.fetchAppStats(); }); const appStatLoaded$ = this.appStatsLoaded.asObservable().pipe(filter(loaded => loaded)); @@ -93,7 +91,7 @@ export class CFHomeCardComponent { this.routeCount$, this.appCount$, this.orgCount$, - this.cfEndpointService.appsPagObs.entities$, + appsPagObs.entities$, appStatLoaded$ ]).pipe( map(() => true) @@ -109,7 +107,7 @@ export class CFHomeCardComponent { } } - // Fetch the app stats - we fetch one at a time + // Fetch the app stats - we fetch two at a time private fetchAppStats() { if (this.appStatsToLoad.length > 0) { const app = this.appStatsToLoad.shift(); @@ -121,9 +119,11 @@ export class CFHomeCardComponent { first() ).subscribe(a => { this.fetchAppStats(); + this.fetchAppStats(); }); } else { this.fetchAppStats(); + this.fetchAppStats(); } } else { this.appStatsLoaded.next(true); diff --git a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html index b53968f927..3b27eca217 100644 --- a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html +++ b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html @@ -33,7 +33,7 @@
-
+
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index d799eff359..b36cf5fee4 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -19,7 +19,7 @@

- +
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index 878b523cbe..4726f22e95 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -1,6 +1,19 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { + AfterViewInit, + Compiler, + Component, + ComponentRef, + EventEmitter, + Injector, + Input, + OnDestroy, + OnInit, + Output, + ViewChild, + ViewContainerRef, +} from '@angular/core'; +import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs'; +import { filter, first, map } from 'rxjs/operators'; import { EntityCatalogSchemas, @@ -11,7 +24,7 @@ import { FavoritesConfigMapper } from '../../../../../../store/src/favorite-conf import { EndpointModel, entityCatalog } from '../../../../../../store/src/public-api'; import { UserFavoriteManager } from '../../../../../../store/src/user-favorite-manager'; import { UserFavoriteEndpoint } from './../../../../../../store/src/types/user-favorites.types'; -import { HomePageCardLayout, LinkMetadata } from './../../home.types'; +import { HomePageCardLayout, HomePageEndpointCard, LinkMetadata } from './../../home.types'; const MAX_FAVS = 5; const MAX_SHORTCUTS = 5; @@ -22,7 +35,9 @@ const MAX_LINKS = 5; templateUrl: './home-page-endpoint-card.component.html', styleUrls: ['./home-page-endpoint-card.component.scss'] }) -export class HomePageEndpointCardComponent implements OnInit { +export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterViewInit { + + @ViewChild('customCard', {read:ViewContainerRef}) customCard: ViewContainerRef; @Input() endpoint: EndpointModel; @@ -61,16 +76,33 @@ export class HomePageEndpointCardComponent implements OnInit { loadSubj = new BehaviorSubject(false); isLoading = false; + private ref: ComponentRef; + private sub: Subscription; + + private canLoad = false; + // Should the Home Card use the whole width, or do we show the links panel as well? fullView = false; constructor( private favoritesConfigMapper: FavoritesConfigMapper, private userFavoriteManager: UserFavoriteManager, + private compiler: Compiler, + private injector: Injector, ) { this.load$ = this.loadSubj.asObservable(); } + ngAfterViewInit(): void { + // Dynamically load the component for the Home Card for this endopoint + const endpointEntity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type) + if (endpointEntity.definition.homeCard && endpointEntity.definition.homeCard.component) { + this.createCard(endpointEntity); + } else { + console.warn(`No endpoint home card for ${this.endpoint.guid}`); + } + } + ngOnInit(): void { // Favorites for this endpoint this.favorites$ = this.userFavoriteManager.getFavoritesForEndpoint(this.endpoint.guid).pipe( @@ -127,17 +159,48 @@ export class HomePageEndpointCardComponent implements OnInit { ); } - public load() { - this.loadSubj.next(true); - this.isLoading = true; + ngOnDestroy() { + if (this.ref) { + this.ref.destroy(); + } + if (this.sub) { + this.sub.unsubscribe(); + } } - public cardLoaded() { - this.loaded.next(this); - this.isLoading = false; + public load() { + this.canLoad = true; + this.isLoading = true; + this.loadCard(); } public updateLayout() { this.layout$.next(this.layout); + + if (this.ref && this.ref.instance) { + (this.ref.instance as any).layout = this._layout; + } + } + + async createCard(endpointEntity: any) { + this.customCard.clear(); + const component = await endpointEntity.definition.homeCard.component(this.compiler, this.injector); + this.ref = this.customCard.createComponent(component); + (this.ref.instance as any).endpoint = this.endpoint; + (this.ref.instance as any).layout = this._layout; + this.loadCard(); + } + + // Ask the card to load itself + loadCard() { + if (this.canLoad && this.ref && this.ref.instance && this.ref.instance.load) { + const loadObs = this.ref.instance.load() || of(true); + this.sub = loadObs.pipe(filter(v => v === true), first()).subscribe(() => this.cardLoaded()); + } + } + + private cardLoaded() { + this.loaded.next(); + this.isLoading = false; } } diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.html b/src/frontend/packages/core/src/features/home/home/home-page.component.html index 2f49e19821..c25e4b388e 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.html @@ -21,7 +21,7 @@

Home

- +
diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.ts index b868e311d9..e0c079c45b 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.ts @@ -1,8 +1,20 @@ import { ScrollDispatcher } from '@angular/cdk/scrolling'; -import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core'; +import { DOCUMENT } from '@angular/common'; +import { + AfterViewInit, + Component, + ElementRef, + HostListener, + Inject, + OnDestroy, + OnInit, + QueryList, + ViewChild, + ViewChildren, +} from '@angular/core'; import { Store } from '@ngrx/store'; import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs'; -import { first, map } from 'rxjs/operators'; +import { debounceTime, filter, first, map, startWith } from 'rxjs/operators'; import { SetHomeCardLayoutAction } from '../../../../../store/src/actions/dashboard-actions'; import { RouterNav } from '../../../../../store/src/actions/router.actions'; @@ -53,13 +65,31 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { notLoadedCardIndices: number[] = []; private sub: Subscription; + private cardChangesSub: Subscription; + private checkLayout = new BehaviorSubject(true); constructor( public endpointsService: EndpointsService, private store: Store, public userFavoriteManager: UserFavoriteManager, private scrollDispatcher: ScrollDispatcher, + @Inject(DOCUMENT) private document ) { + // Redirect to /applications if not enabled + endpointsService.disablePersistenceFeatures$.pipe( + map(off => { + if (off) { + store.dispatch(new RouterNav({ + path: ['applications'], + extras: { + replaceUrl: true + } + })); + } + }), + first() + ).subscribe(); + this.layouts$ = of(this.layouts); this.layout$ = this.layout.asObservable(); this.allEndpointIds$ = this.endpointsService.endpoints$.pipe( @@ -69,17 +99,17 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { // Only show endpoints that have Home Card metadata this.endpoints$ = combineLatest([this.endpointsService.endpoints$, userFavoriteManager.getAllFavorites()]).pipe( - map(([endpoints, [favGroups, favs]]) => { - const ordered = this.orderEndpoints(endpoints, favGroups); - return ordered.filter(ep => { - const defn = entityCatalog.getEndpoint(ep.cnsi_type, ep.sub_type); - return !!defn.definition.homeCard; + map(([endpoints, [favGroups, favs]]) => { + const ordered = this.orderEndpoints(endpoints, favGroups); + return ordered.filter(ep => { + const defn = entityCatalog.getEndpoint(ep.cnsi_type, ep.sub_type); + return !!defn.definition.homeCard; + }); }) - }) ); // Set an initial layout - this.layout.next(new HomePageCardLayout(1, 1)); + this.layout.next(this.getLayout(1, 1)); this.store.select(selectDashboardState).pipe( map(dashboardState => dashboardState.homeLayout || 0), @@ -88,82 +118,81 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { const selected = this.layouts.find(hpcl => hpcl && hpcl.id === id) || this.layouts[0]; this.onChangeLayout(selected); }) - - // Redirect to /applications if not enabled - endpointsService.disablePersistenceFeatures$.pipe( - map(off => { - if (off) { - store.dispatch(new RouterNav({ - path: ['applications'], - extras: { - replaceUrl: true - } - })); - } - }), - first() - ).subscribe(); - } ngOnInit() { - // Listen for scroll events - used to load cards as they come into view - this.sub = this.scrollDispatcher.scrolled().subscribe((x:any) => { - const el = x.elementRef.nativeElement; - this.handleScroll(el.scrollTop); - }); + const check$ = this.checkLayout.asObservable().pipe(filter(v => v)); + const scroll$ = this.scrollDispatcher.scrolled().pipe(map((e: any) => { + const el = e.elementRef.nativeElement; + return el.scrollTop; + }), startWith(0)); + + // Load cards as they come into view + this.sub = combineLatest([scroll$, check$]).pipe(debounceTime(200)).subscribe(([scrollTop, check]) => { + // User has scrolled - check the remaining cards that have not been loaded to see if any are now visible and shoule be loaded + // Only load the first one - after that one has loaded, we'll call this method again and check for the next one + const remaining = []; + let processedCard = false; + for (const index of this.notLoadedCardIndices) { + const cardElement = this.endpointElements.toArray()[index] as ElementRef; + const top = cardElement.nativeElement.offsetTop; + const height = this.endpointsPanel.nativeElement.offsetParent.offsetHeight; + const bottom = scrollTop + height; + if (!processedCard && top >= scrollTop && top <= bottom) { + const card = this.endpointCards.toArray()[index]; + card.load(); + processedCard = true; + } else { + remaining.push(index); + } + }; + this.notLoadedCardIndices = remaining; + }) } ngOnDestroy() { if (this.sub) { this.sub.unsubscribe(); } + if (this.cardChangesSub) { + this.cardChangesSub.unsubscribe(); + } } ngAfterViewInit(): void { - this.endpointElements.changes.subscribe(cards => { - this.notLoadedCardIndices = []; - for(let i=0;i< cards.length; i++) { - this.notLoadedCardIndices.push(i); - } - // Schedule a check for cards that are visible and should be loaded - setTimeout(() => this.handleScroll(), 100); - }); + this.cardChangesSub = this.endpointElements.changes.subscribe(cards => this.setCardsToLoad(cards)); + if (this.endpointElements.toArray().length > 0) { + this.setCardsToLoad(this.endpointElements.toArray()); + } + } + + setCardsToLoad(cards: any[]) { + this.notLoadedCardIndices = []; + for(let i=0;i< cards.length; i++) { + this.notLoadedCardIndices.push(i); + } + setTimeout(() => this.checkCardsInView(), 1); } // This is called after a card has loaded - we call the scroll handler again // to check if there are more cards that are visible and thus can be loaded cardLoaded() { - console.log('Card loaded'); - this.handleScroll(); + this.checkCardsInView(); } - // User has scrolled - check the remaining cards that have not been loaded - // to see if any are now visible and shoule be loaded - // Only load the first one - after that one has loaded, we'll call this method again - // and check for the next one - handleScroll(scrollTop: number = 0) { - const remaining = []; - let processedCard = false; - for (const index of this.notLoadedCardIndices) { - const cardElement = this.endpointElements.toArray()[index] as ElementRef; - const top = cardElement.nativeElement.offsetTop; - const height = this.endpointsPanel.nativeElement.offsetParent.offsetHeight; - const bottom = scrollTop + height; - if (!processedCard && top >= scrollTop && top <= bottom) { - const card = this.endpointCards.toArray()[index]; - card.load(); - processedCard = true; - } else { - remaining.push(index); - } - }; - this.notLoadedCardIndices = remaining; + @HostListener('window:resize') + onResize() { + // If we resize the window and make it larger then new cards may come into view + this.checkCardsInView(); + } + + // Check the cards in view + checkCardsInView() { + this.checkLayout.next(true); } + // The layout was changed public onChangeLayout(layout: HomePageCardLayout) { - console.log('onChangeLayout'); - console.log(layout); this.layoutID = layout.id; // If the layout is automatic, then adjust based on number of things to show @@ -178,8 +207,8 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { this.store.dispatch(new SetHomeCardLayoutAction(this.layoutID)); // Ensure we check again if any cards are now visible - console.log('Handle scroll'); - this.handleScroll(); + // Schedule the check so it happens afer the cards have been laid out + setTimeout(() => this.checkCardsInView(), 1); }); } From 5aa01ba4ad2ddcee35f503eeee2bb691f9fae791 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 5 Nov 2020 19:46:45 +0000 Subject: [PATCH 08/74] Tidy ups and improvemts to async loading --- .../dashboard-base.component.html | 5 +- .../dashboard-base.component.scss | 4 + .../dashboard-base.component.ts | 10 ++- .../core/src/features/home/home.module.ts | 4 +- .../favorites-side-panel.component.html | 7 ++ .../favorites-side-panel.component.scss | 7 ++ .../favorites-side-panel.component.spec.ts | 25 ++++++ .../favorites-side-panel.component.ts | 23 +++++ .../home/home/home-page-card.directive.ts | 90 ------------------- .../home-page-endpoint-card.component.html | 5 +- .../home-page-endpoint-card.component.scss | 12 ++- ...me-page-endpoint-card.component.theme.scss | 2 +- .../home-page-endpoint-card.component.ts | 12 +++ .../home-shortcuts.component.html | 2 +- .../src/shared/services/side-panel.service.ts | 30 +++++-- 15 files changed, 134 insertions(+), 104 deletions(-) create mode 100644 src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.html create mode 100644 src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.scss create mode 100644 src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.spec.ts create mode 100644 src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.ts delete mode 100644 src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts diff --git a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html index 3b27eca217..d9b88d2fee 100644 --- a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html +++ b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html @@ -42,9 +42,10 @@ -
+
diff --git a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.scss b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.scss index 34b3de01a3..0618205624 100644 --- a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.scss +++ b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.scss @@ -63,6 +63,10 @@ $app-header-height: 56px; min-width: auto; } } + &__side-narrow { + max-width: 400px; + min-width: 400px; + } } .page-content { diff --git a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.ts b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.ts index b7da02c398..f223a816ca 100644 --- a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.ts +++ b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.ts @@ -17,7 +17,7 @@ import { stratosEntityCatalog } from '../../../../../store/src/stratos-entity-ca import { CustomizationService } from '../../../core/customizations.types'; import { EndpointsService } from '../../../core/endpoints.service'; import { IHeaderBreadcrumbLink } from '../../../shared/components/page-header/page-header.types'; -import { SidePanelService } from '../../../shared/services/side-panel.service'; +import { SidePanelMode, SidePanelService } from '../../../shared/services/side-panel.service'; import { TabNavService } from '../../../tab-nav.service'; import { IPageSideNavTab } from '../page-side-nav/page-side-nav.component'; import { PageHeaderService } from './../../../core/page-header-service/page-header.service'; @@ -53,6 +53,9 @@ export class DashboardBaseComponent implements OnInit, OnDestroy, AfterViewInit @ViewChild('content') public content; + // Slide-in side panel mode + sidePanelMode: SidePanelMode = 0; + constructor( public pageHeaderService: PageHeaderService, private store: Store, @@ -94,6 +97,11 @@ export class DashboardBaseComponent implements OnInit, OnDestroy, AfterViewInit }) ); + this.sidePanelService.previewMode$.subscribe(mode => { + console.log('Mode is ' + mode); + this.sidePanelMode = mode + }); + this.mobileSub = this.isMobile$ .subscribe(isMobile => isMobile ? this.store.dispatch(new EnableMobileNav()) : this.store.dispatch(new DisableMobileNav())); } diff --git a/src/frontend/packages/core/src/features/home/home.module.ts b/src/frontend/packages/core/src/features/home/home.module.ts index 005e3e587f..702a3ae973 100644 --- a/src/frontend/packages/core/src/features/home/home.module.ts +++ b/src/frontend/packages/core/src/features/home/home.module.ts @@ -4,7 +4,7 @@ import { CoreModule, SharedModule } from '@stratosui/core'; import { MDAppModule } from './../../core/md.module'; import { FavoritesMetaCardComponent } from './home/favorites-meta-card/favorites-meta-card.component'; -import { HomePageCardDirective } from './home/home-page-card.directive'; +import { FavoritesSidePanelComponent } from './home/favorites-side-panel/favorites-side-panel.component'; import { HomePageEndpointCardComponent } from './home/home-page-endpoint-card/home-page-endpoint-card.component'; import { HomePageComponent } from './home/home-page.component'; import { HomeShortcutsComponent } from './home/home-shortcuts/home-shortcuts.component'; @@ -19,10 +19,10 @@ import { HomeShortcutsComponent } from './home/home-shortcuts/home-shortcuts.com ], declarations: [ HomePageComponent, - HomePageCardDirective, HomePageEndpointCardComponent, FavoritesMetaCardComponent, HomeShortcutsComponent, + FavoritesSidePanelComponent, ], exports: [ FavoritesMetaCardComponent, diff --git a/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.html b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.html new file mode 100644 index 0000000000..76f906bf15 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.html @@ -0,0 +1,7 @@ + + +
+ +
+ +
diff --git a/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.scss b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.scss new file mode 100644 index 0000000000..c0137eecc0 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.scss @@ -0,0 +1,7 @@ +.fav-side-panel { + &__card { + display: flex; + flex-direction: column; + margin-bottom: 6px; + } +} \ No newline at end of file diff --git a/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.spec.ts b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.spec.ts new file mode 100644 index 0000000000..ba66b4964d --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FavoritesSidePanelComponent } from './favorites-side-panel.component'; + +describe('FavoritesSidePanelComponent', () => { + let component: FavoritesSidePanelComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ FavoritesSidePanelComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FavoritesSidePanelComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.ts b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.ts new file mode 100644 index 0000000000..f2f60d2ed6 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.ts @@ -0,0 +1,23 @@ +import { Component } from '@angular/core'; +import { Observable } from 'rxjs'; + +import { PreviewableComponent } from '../../../../shared/previewable-component'; + +@Component({ + selector: 'app-favorites-side-panel', + templateUrl: './favorites-side-panel.component.html', + styleUrls: ['./favorites-side-panel.component.scss'] +}) +export class FavoritesSidePanelComponent implements PreviewableComponent { + + favorites$: Observable; + name: string; + + constructor() { } + + setProps(props: { [key: string]: any; }): void { + this.favorites$ = props.favorites$ + this.name = props.endpoint.name; + } + +} diff --git a/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts b/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts deleted file mode 100644 index aeb2bd0a40..0000000000 --- a/src/frontend/packages/core/src/features/home/home/home-page-card.directive.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { - Compiler, - ComponentRef, - Directive, - EventEmitter, - Injector, - Input, - OnDestroy, - OnInit, - Output, - ViewContainerRef, -} from '@angular/core'; -import { of, Subscription } from 'rxjs'; -import { filter, first } from 'rxjs/operators'; - -import { EndpointModel, entityCatalog } from '../../../../../store/src/public-api'; -import { HomePageCardLayout, HomePageEndpointCard } from './../home.types'; - -@Directive({ - selector: '[appHomeCard]', -}) -export class HomePageCardDirective implements OnInit, OnDestroy { - - private canLoad = false; - - private _layout: HomePageCardLayout; - - @Input() appHomeCard: EndpointModel; - - @Input() set layout(value: HomePageCardLayout) { - this._layout = value; - if (this.ref) { - this.ref.instance.layout = value; - } - }; - - @Input() set load(value: boolean) { - this.canLoad = value; - this.loadCard(); - } - - @Output() loaded = new EventEmitter(); - - private ref: ComponentRef; - - private sub: Subscription; - - constructor( - private viewContainerRef: ViewContainerRef, - private compiler: Compiler, - private injector: Injector, - ) { } - - ngOnInit() { - // Dynamically load the component for the Home Card - const endpointEntity = entityCatalog.getEndpoint(this.appHomeCard.cnsi_type, this.appHomeCard.sub_type) - if (endpointEntity.definition.homeCard && endpointEntity.definition.homeCard.component) { - this.createCard(endpointEntity); - } else { - console.warn(`'No endpoint home card for ${this.appHomeCard.guid}`); - } - } - - ngOnDestroy() { - if (this.ref) { - this.ref.destroy(); - } - if (this.sub) { - this.sub.unsubscribe(); - } - } - - // Ask the card to load itself - loadCard() { - if (this.canLoad && this.ref && this.ref.instance && this.ref.instance.load) { - const loadObs = this.ref.instance.load() || of(true); - this.sub = loadObs.pipe(filter(v => v === true), first()).subscribe(() => this.loaded.next(null)); - } - } - - async createCard(endpointEntity: any) { - this.viewContainerRef.clear(); - const component = await endpointEntity.definition.homeCard.component(this.compiler, this.injector); - this.ref = this.viewContainerRef.createComponent(component); - (this.ref.instance as any).endpoint = this.appHomeCard; - (this.ref.instance as any).layout = this._layout; - this.loadCard(); - } - -} diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index b36cf5fee4..ff4aa97dcd 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -23,7 +23,10 @@

-
Favorites
+
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss index 8ac64dc61d..45772733c0 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss @@ -143,9 +143,19 @@ &__fav-none { margin-left: 12px; } - &__fav-title { + &__fav-title-panel { + display: flex; margin-bottom: 10px; } + &__fav-title { + flex: 1; + } + &__fav-more { + font-size: 12px; + A { + cursor: pointer; + } + } &__fav-card { display: flex; flex-direction: column; diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.theme.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.theme.scss index 6e0b7bfc3e..a02aed0ccd 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.theme.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.theme.scss @@ -2,7 +2,7 @@ $foreground: map-get($theme, foreground); .fav-meta-card { - border: 1px solid mat-color($foreground, divider); + //border: 1px solid mat-color($foreground, divider); &__content { border-top: 1px solid mat-color($foreground, divider); } diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index 4726f22e95..fa1a4e1383 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -23,6 +23,8 @@ import { import { FavoritesConfigMapper } from '../../../../../../store/src/favorite-config-mapper'; import { EndpointModel, entityCatalog } from '../../../../../../store/src/public-api'; import { UserFavoriteManager } from '../../../../../../store/src/user-favorite-manager'; +import { SidePanelMode, SidePanelService } from '../../../../shared/services/side-panel.service'; +import { FavoritesSidePanelComponent } from '../favorites-side-panel/favorites-side-panel.component'; import { UserFavoriteEndpoint } from './../../../../../../store/src/types/user-favorites.types'; import { HomePageCardLayout, HomePageEndpointCard, LinkMetadata } from './../../home.types'; @@ -57,6 +59,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi @Output() loaded = new EventEmitter(); favorites$: Observable; + hiddenFavorites = 0; shortcuts: HomeCardShortcut[]; @@ -87,6 +90,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi constructor( private favoritesConfigMapper: FavoritesConfigMapper, private userFavoriteManager: UserFavoriteManager, + private sidePanelService: SidePanelService, private compiler: Compiler, private injector: Injector, ) { @@ -134,6 +138,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi map(([favs, layout]) => { let shortcuts: HomeCardShortcut[] = this.shortcuts || []; const totalShortcuts = shortcuts.length; + this.hiddenFavorites = favs.length - MAX_FAVS; // Based on the layout, adjust the numbers returned if (layout.y > 1) { if (favs.length > MAX_FAVS) { @@ -203,4 +208,11 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi this.loaded.next(); this.isLoading = false; } + + public showFavoritesPanel() { + this.sidePanelService.showMode(SidePanelMode.Narrow, FavoritesSidePanelComponent, { + endpoint: this.endpoint, + favorites$: this.favorites$ + }); + } } diff --git a/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.html b/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.html index 9c87aa5a5a..d9f0a887dc 100644 --- a/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-shortcuts/home-shortcuts.component.html @@ -1,4 +1,4 @@ -
+
Shortcuts
diff --git a/src/frontend/packages/core/src/shared/services/side-panel.service.ts b/src/frontend/packages/core/src/shared/services/side-panel.service.ts index 2b86182a17..90217bd718 100644 --- a/src/frontend/packages/core/src/shared/services/side-panel.service.ts +++ b/src/frontend/packages/core/src/shared/services/side-panel.service.ts @@ -11,6 +11,12 @@ import { Router } from '@angular/router'; import { asapScheduler, BehaviorSubject, Observable, Subject } from 'rxjs'; import { filter, observeOn, publishReplay, refCount, tap } from 'rxjs/operators'; +export enum SidePanelMode { + Modal = 0, + Normal = 1, + Narrow = 2, +} + /** * Service to allow the overlay side panel to be shown or hidden. * @@ -23,8 +29,8 @@ export class SidePanelService { private openedSubject: BehaviorSubject; public opened$: Observable; - private previewModeSubject: BehaviorSubject; - public previewMode$: Observable; + private previewModeSubject: BehaviorSubject; + public previewMode$: Observable; private container: ViewContainerRef; @@ -36,7 +42,7 @@ export class SidePanelService { this.openedSubject = new BehaviorSubject(false); this.opened$ = this.observeSubject(this.openedSubject); - this.previewModeSubject = new BehaviorSubject(false); + this.previewModeSubject = new BehaviorSubject(-1); this.previewMode$ = this.observeSubject(this.previewModeSubject); this.setupRouterListener(); @@ -63,7 +69,21 @@ export class SidePanelService { } this.render(component, props, componentFactoryResolver); - this.previewModeSubject.next(true); + this.previewModeSubject.next(SidePanelMode.Normal); + this.open(); + } + + /** + * Show the preview panel in a preview style - does not overlap title bar and colours are more muted + */ + public showMode( + mode: SidePanelMode, component: object, props?: { [key: string]: any }, componentFactoryResolver?: ComponentFactoryResolver) { + if (!this.container) { + throw new Error('SidePanelService: container must be set'); + } + + this.render(component, props, componentFactoryResolver); + this.previewModeSubject.next(mode); this.open(); } @@ -76,7 +96,7 @@ export class SidePanelService { } this.render(component, props, componentFactoryResolver); - this.previewModeSubject.next(false); + this.previewModeSubject.next(SidePanelMode.Modal); this.open(); } From 6371a8289e53b3fdb55ee9d5685f7444cbe880dc Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 5 Nov 2020 20:00:12 +0000 Subject: [PATCH 09/74] Fix progress spinner alignment --- .../home-page-endpoint-card.component.html | 10 ++++++---- .../home-page-endpoint-card.component.scss | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index ff4aa97dcd..7508762604 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -11,10 +11,12 @@

{{ definition.label }}
- -
- -
+
+ +
+
+ +
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss index 45772733c0..8d2af141e0 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss @@ -141,7 +141,8 @@ } } &__fav-none { - margin-left: 12px; + opacity: 0.7; + text-align: center; } &__fav-title-panel { display: flex; From 00a6c5be39c971cc93289d2bbaef29834b23c40f Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 07:39:03 +0000 Subject: [PATCH 10/74] Unscubribe --- .../dashboard/dashboard-base/dashboard-base.component.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.ts b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.ts index f223a816ca..66be597449 100644 --- a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.ts +++ b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.ts @@ -42,6 +42,7 @@ export class DashboardBaseComponent implements OnInit, OnDestroy, AfterViewInit public noMargin$: Observable; private closeSub: Subscription; private mobileSub: Subscription; + private sidePanelSub: Subscription; private drawer: MatDrawer; public iconModeOpen = false; public sideNavWidth = 54; @@ -97,8 +98,7 @@ export class DashboardBaseComponent implements OnInit, OnDestroy, AfterViewInit }) ); - this.sidePanelService.previewMode$.subscribe(mode => { - console.log('Mode is ' + mode); + this.sidePanelSub = this.sidePanelService.previewMode$.subscribe(mode => { this.sidePanelMode = mode }); @@ -162,6 +162,7 @@ export class DashboardBaseComponent implements OnInit, OnDestroy, AfterViewInit this.mobileSub.unsubscribe(); this.closeSub.unsubscribe(); this.sidePanelService.unsetContainer(); + this.sidePanelSub.unsubscribe(); } isNoMarginView(route: ActivatedRouteSnapshot): boolean { From c715a0c618778e894c5b17906f9dbb46b89771ad Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 08:27:06 +0000 Subject: [PATCH 11/74] Minor bug fixes and a few tidy ups --- .../home-page-endpoint-card.component.html | 7 ++- .../home-page-endpoint-card.component.scss | 7 +++ .../home-page-endpoint-card.component.ts | 54 ++++++++++++------- .../features/home/home/home-page.component.ts | 12 +++-- 4 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index 7508762604..be34930682 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -21,7 +21,12 @@

+
+
+
+ +
@@ -36,7 +41,7 @@

No favorites
- +

diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss index 8d2af141e0..e0de21dd33 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss @@ -162,4 +162,11 @@ flex-direction: column; margin-bottom: 5px; } + &__shortcuts-left { + display: flex; + flex-direction: column; + margin-bottom: 10px; + margin-top: -10px; + padding: 0 16px; + } } diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index fa1a4e1383..83c6ae8fdb 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -28,7 +28,9 @@ import { FavoritesSidePanelComponent } from '../favorites-side-panel/favorites-s import { UserFavoriteEndpoint } from './../../../../../../store/src/types/user-favorites.types'; import { HomePageCardLayout, HomePageEndpointCard, LinkMetadata } from './../../home.types'; -const MAX_FAVS = 5; +const MAX_FAVS_NORMAL = 15; +const MAX_FAVS_COMPACT = 5; +const CUTOFF_SHOW_SHORTCUTS_ON_LEFT = 10; const MAX_SHORTCUTS = 5; const MAX_LINKS = 5; @@ -59,7 +61,6 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi @Output() loaded = new EventEmitter(); favorites$: Observable; - hiddenFavorites = 0; shortcuts: HomeCardShortcut[]; @@ -84,6 +85,10 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi private canLoad = false; + // Should we show shortcuts on the side or udner the manin panel? + showShortcutsOnSide = true; + hiddenFavorites = 0; + // Should the Home Card use the whole width, or do we show the links panel as well? fullView = false; @@ -97,7 +102,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi this.load$ = this.loadSubj.asObservable(); } - ngAfterViewInit(): void { + ngAfterViewInit() { // Dynamically load the component for the Home Card for this endopoint const endpointEntity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type) if (endpointEntity.definition.homeCard && endpointEntity.definition.homeCard.component) { @@ -107,7 +112,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi } } - ngOnInit(): void { + ngOnInit() { // Favorites for this endpoint this.favorites$ = this.userFavoriteManager.getFavoritesForEndpoint(this.endpoint.guid).pipe( map(f => { @@ -137,12 +142,17 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi this.links$ = combineLatest([this.favorites$, this.layout$.asObservable()]).pipe( map(([favs, layout]) => { let shortcuts: HomeCardShortcut[] = this.shortcuts || []; + + const max = (layout.y > 1) ? MAX_FAVS_COMPACT : MAX_FAVS_NORMAL; const totalShortcuts = shortcuts.length; - this.hiddenFavorites = favs.length - MAX_FAVS; + this.hiddenFavorites = favs.length - max; + // Based on the layout, adjust the numbers returned if (layout.y > 1) { - if (favs.length > MAX_FAVS) { - favs = favs.slice(0, MAX_FAVS); + // Compact card view + this.showShortcutsOnSide = true; + if (favs.length > max) { + favs = favs.slice(0, max); } if (totalShortcuts > MAX_SHORTCUTS) { shortcuts = this.shortcuts.slice(0, MAX_SHORTCUTS); @@ -155,6 +165,12 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi } shortcuts = this.shortcuts.slice(0, limit); } + } else { + // Full card view - move the shortcuts into the main left panel if we have more + // than a certain number of favorites to also show + if (favs.length >= CUTOFF_SHOW_SHORTCUTS_ON_LEFT) { + this.showShortcutsOnSide = false; + } } return { favs, @@ -173,12 +189,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi } } - public load() { - this.canLoad = true; - this.isLoading = true; - this.loadCard(); - } - + // Layout has changed public updateLayout() { this.layout$.next(this.layout); @@ -196,19 +207,24 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi this.loadCard(); } + // Load the card + public load() { + this.canLoad = true; + this.loadCard(); + } + // Ask the card to load itself loadCard() { if (this.canLoad && this.ref && this.ref.instance && this.ref.instance.load) { + this.isLoading = true; const loadObs = this.ref.instance.load() || of(true); - this.sub = loadObs.pipe(filter(v => v === true), first()).subscribe(() => this.cardLoaded()); + this.sub = loadObs.pipe(filter(v => v === true), first()).subscribe(() => { + this.loaded.next(); + this.isLoading = false; + }); } } - private cardLoaded() { - this.loaded.next(); - this.isLoading = false; - } - public showFavoritesPanel() { this.sidePanelService.showMode(SidePanelMode.Narrow, FavoritesSidePanelComponent, { endpoint: this.endpoint, diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.ts index e0c079c45b..7cc326f9dd 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.ts @@ -135,10 +135,16 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { let processedCard = false; for (const index of this.notLoadedCardIndices) { const cardElement = this.endpointElements.toArray()[index] as ElementRef; - const top = cardElement.nativeElement.offsetTop; + const cardTop = cardElement.nativeElement.offsetTop; + const cardBottom = cardTop + cardElement.nativeElement.offsetHeight; const height = this.endpointsPanel.nativeElement.offsetParent.offsetHeight; - const bottom = scrollTop + height; - if (!processedCard && top >= scrollTop && top <= bottom) { + const scrollBottom = scrollTop + height; + + console.log(index + ' ' + cardTop + ' ' + cardBottom + ' ' + scrollTop + ' ' + scrollBottom); + // Check if the card is in view - either its top or bottom must be withtin he visible scroll area + if (!processedCard && + (cardTop >= scrollTop && cardTop <= scrollBottom) || + (cardBottom >= scrollTop && cardBottom <= scrollBottom)) { const card = this.endpointCards.toArray()[index]; card.load(); processedCard = true; From d2b24d949a2116fd6678556a6bea8a847abd03d5 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 10:19:12 +0000 Subject: [PATCH 12/74] Further tidy ups --- .../cfhome-card/cfhome-card.component.html | 2 +- .../home/cfhome-card/cfhome-card.component.ts | 7 ++-- .../home-page-endpoint-card.component.html | 3 ++ .../home-page-endpoint-card.component.scss | 3 ++ .../home-page-endpoint-card.component.ts | 11 ++++- .../features/home/home/home-page.component.ts | 41 ++++++++----------- .../home/kubernetes-home-card.component.ts | 2 - 7 files changed, 36 insertions(+), 33 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html index c687bbeb84..01130f980e 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html @@ -17,7 +17,7 @@ - + diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts index e42fed95b7..e162fdd1ad 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core'; import { Store } from '@ngrx/store'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; -import { filter, first, map, pairwise } from 'rxjs/operators'; +import { filter, first, map, pairwise, tap } from 'rxjs/operators'; import { PaginationMonitorFactory } from '../../../../../store/src/monitors/pagination-monitor.factory'; import { EndpointModel } from '../../../../../store/src/public-api'; @@ -58,6 +58,7 @@ export class CFHomeCardComponent { routeCount$: Observable; cardLoaded = false; + statsLoaded = false; private appStatsLoaded = new BehaviorSubject(false); private appStatsToLoad: APIResource[] = []; @@ -86,7 +87,6 @@ export class CFHomeCardComponent { }); const appStatLoaded$ = this.appStatsLoaded.asObservable().pipe(filter(loaded => loaded)); - return combineLatest([ this.routeCount$, this.appCount$, @@ -94,7 +94,8 @@ export class CFHomeCardComponent { appsPagObs.entities$, appStatLoaded$ ]).pipe( - map(() => true) + map(() => true), + tap(() => { this.statsLoaded = true; }) ); } diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index be34930682..ba214afad0 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -14,6 +14,9 @@

+
+ warning +
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss index e0de21dd33..f1e49428d3 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss @@ -46,6 +46,9 @@ margin-left: 20px; opacity: 0.7; } + &__error { + display: flex; + } // Check if these are needed &__type { $type-height: 20px; diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index 83c6ae8fdb..c15bb1be1a 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -13,7 +13,7 @@ import { ViewContainerRef, } from '@angular/core'; import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs'; -import { filter, first, map } from 'rxjs/operators'; +import { filter, first, map, timeout } from 'rxjs/operators'; import { EntityCatalogSchemas, @@ -79,6 +79,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi load$: Observable; loadSubj = new BehaviorSubject(false); isLoading = false; + isError = false; private ref: ComponentRef; private sub: Subscription; @@ -218,9 +219,15 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi if (this.canLoad && this.ref && this.ref.instance && this.ref.instance.load) { this.isLoading = true; const loadObs = this.ref.instance.load() || of(true); - this.sub = loadObs.pipe(filter(v => v === true), first()).subscribe(() => { + + // Timeout after 10 seconds + this.sub = loadObs.pipe(timeout(10000), filter(v => v === true), first()).subscribe(() => { + this.loaded.next(); + this.isLoading = false; + }, () => { this.loaded.next(); this.isLoading = false; + this.isError = true; }); } } diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.ts index 7cc326f9dd..922ba5db24 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.ts @@ -63,6 +63,8 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { @ViewChildren('endpointCard') endpointElements: QueryList; notLoadedCardIndices: number[] = []; + cardsToLoad: HomePageEndpointCardComponent[] = []; + isLoadingACard = false; private sub: Subscription; private cardChangesSub: Subscription; @@ -132,22 +134,18 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { // User has scrolled - check the remaining cards that have not been loaded to see if any are now visible and shoule be loaded // Only load the first one - after that one has loaded, we'll call this method again and check for the next one const remaining = []; - let processedCard = false; + const processedCard = false; for (const index of this.notLoadedCardIndices) { const cardElement = this.endpointElements.toArray()[index] as ElementRef; const cardTop = cardElement.nativeElement.offsetTop; const cardBottom = cardTop + cardElement.nativeElement.offsetHeight; const height = this.endpointsPanel.nativeElement.offsetParent.offsetHeight; const scrollBottom = scrollTop + height; - - console.log(index + ' ' + cardTop + ' ' + cardBottom + ' ' + scrollTop + ' ' + scrollBottom); // Check if the card is in view - either its top or bottom must be withtin he visible scroll area - if (!processedCard && - (cardTop >= scrollTop && cardTop <= scrollBottom) || - (cardBottom >= scrollTop && cardBottom <= scrollBottom)) { + if ((cardTop >= scrollTop && cardTop <= scrollBottom) || (cardBottom >= scrollTop && cardBottom <= scrollBottom)) { const card = this.endpointCards.toArray()[index]; - card.load(); - processedCard = true; + this.cardsToLoad.push(card); + this.processCardsToLoad(); } else { remaining.push(index); } @@ -156,6 +154,14 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { }) } + processCardsToLoad() { + if (!this.isLoadingACard && this.cardsToLoad.length > 0) { + const nextCardToLoad = this.cardsToLoad.shift(); + this.isLoadingACard = true; + nextCardToLoad.load(); + } + } + ngOnDestroy() { if (this.sub) { this.sub.unsubscribe(); @@ -183,6 +189,8 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { // This is called after a card has loaded - we call the scroll handler again // to check if there are more cards that are visible and thus can be loaded cardLoaded() { + this.isLoadingACard = false; + this.processCardsToLoad(); this.checkCardsInView(); } @@ -284,21 +292,4 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { private getLayout(x: number, y: number): HomePageCardLayout { return this.layouts.find(item => item && item.x === x && item.y === y); } - - // Validate all of the entities one by one and update if they are no longer valid - // validate() { - // this.favoriteGroups$.pipe(first()).subscribe(f => { - // console.log('Validating favourites'); - // console.log(f); - // f.forEach(group => { - // console.log(group); - // // Maybe we need to check the enpoint via the health check first? - // // Check each entity in turn - // group.entities.forEach(entity => { - // console.log(entity); - // }); - // }) - // }); - // } } - diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts index 2605c4e104..0c644ab43b 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts @@ -56,10 +56,8 @@ export class KubernetesHomeCardComponent implements OnInit { iconFont: 'stratos-icons' } ]; - } - // Card is instructed to load its view by the container, whn it is visible load(): Observable { const guid = this.endpoint.guid; From b4a28553f5d610006818686643b814be896fad01 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 10:30:54 +0000 Subject: [PATCH 13/74] Fix front end unit tests --- .../home/cfhome-card/cfhome-card.component.spec.ts | 4 +++- .../favorites-meta-card.component.spec.ts | 2 +- .../home-page-endpoint-card.component.spec.ts | 13 ++++++++++++- .../packages/store/testing/src/store-test-helper.ts | 1 + 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.spec.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.spec.ts index 2ab0ef1072..bd2135b420 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.spec.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.spec.ts @@ -1,5 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { generateCfBaseTestModules } from '../../../../test-framework/cloud-foundry-endpoint-service.helper'; import { CFHomeCardComponent } from './cfhome-card.component'; describe('CFHomeCardComponent', () => { @@ -8,7 +9,8 @@ describe('CFHomeCardComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ CFHomeCardComponent ] + declarations: [ CFHomeCardComponent ], + imports: generateCfBaseTestModules() }) .compileComponents(); })); diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.spec.ts b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.spec.ts index e76f49a277..72ac89c8f4 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.spec.ts +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.spec.ts @@ -1,6 +1,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { BaseTestModules } from '../../../../test-framework/core-test.helper'; +import { BaseTestModules } from '../../../../../test-framework/core-test.helper'; import { FavoritesMetaCardComponent } from './favorites-meta-card.component'; describe('FavoritesMetaCardComponent', () => { diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts index 75f01e1ce4..87ae1842b4 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts @@ -1,5 +1,9 @@ +import { CommonModule } from '@angular/common'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { createBasicStoreModule } from '../../../../../../store/testing/public-api'; +import { CoreTestingModule } from '../../../../../test-framework/core-test.modules'; +import { CoreModule, SharedModule } from '../../../../public-api'; import { HomePageEndpointCardComponent } from './home-page-endpoint-card.component'; describe('HomePageEndpointCardComponent', () => { @@ -8,7 +12,14 @@ describe('HomePageEndpointCardComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ HomePageEndpointCardComponent ] + declarations: [ HomePageEndpointCardComponent ], + imports: [ + CommonModule, + CoreModule, + SharedModule, + CoreTestingModule, + createBasicStoreModule() + ], }) .compileComponents(); })); diff --git a/src/frontend/packages/store/testing/src/store-test-helper.ts b/src/frontend/packages/store/testing/src/store-test-helper.ts index ad0942e40f..2de29a2b45 100644 --- a/src/frontend/packages/store/testing/src/store-test-helper.ts +++ b/src/frontend/packages/store/testing/src/store-test-helper.ts @@ -167,6 +167,7 @@ function getDefaultInitialTestStratosStoreState() { themeKey: null, headerEventMinimized: true, gravatarEnabled: false, + homeLayout: 0, }, lists: {}, routing: { From cd1fa93f876a8a7d6930d3d573585b3bc637db16 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 11:24:39 +0000 Subject: [PATCH 14/74] Unit test fix --- .../src/features/home/cfhome-card/cfhome-card.component.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts index e162fdd1ad..5f5d4ed50d 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts @@ -66,7 +66,10 @@ export class CFHomeCardComponent { constructor( private store: Store, private pmf: PaginationMonitorFactory, - ) {} + ) { + // Set a default layout + this._layout = new HomePageCardLayout(1, 1); + } // Card is instructed to load its view by the container, whn it is visible load(): Observable { From 7417f856532d93262adbae7506743322fb0f6a24 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 11:35:03 +0000 Subject: [PATCH 15/74] Further unit tests fixes --- .../home-page-endpoint-card.component.html | 2 +- .../home-page-endpoint-card.component.spec.ts | 6 ++++++ .../home-page-endpoint-card.component.ts | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index ba214afad0..5db24935bd 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -1,7 +1,7 @@
- +

diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts index 87ae1842b4..30d9db2f0c 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts @@ -1,9 +1,11 @@ import { CommonModule } from '@angular/common'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; import { createBasicStoreModule } from '../../../../../../store/testing/public-api'; import { CoreTestingModule } from '../../../../../test-framework/core-test.modules'; import { CoreModule, SharedModule } from '../../../../public-api'; +import { SidePanelService } from '../../../../shared/services/side-panel.service'; import { HomePageEndpointCardComponent } from './home-page-endpoint-card.component'; describe('HomePageEndpointCardComponent', () => { @@ -17,9 +19,13 @@ describe('HomePageEndpointCardComponent', () => { CommonModule, CoreModule, SharedModule, + RouterTestingModule, CoreTestingModule, createBasicStoreModule() ], + providers: [ + SidePanelService + ] }) .compileComponents(); })); diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index c15bb1be1a..795fd2b41d 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -104,6 +104,9 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi } ngAfterViewInit() { + if (!this.endpoint) { + return; + } // Dynamically load the component for the Home Card for this endopoint const endpointEntity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type) if (endpointEntity.definition.homeCard && endpointEntity.definition.homeCard.component) { @@ -114,6 +117,9 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi } ngOnInit() { + if (!this.endpoint) { + return; + } // Favorites for this endpoint this.favorites$ = this.userFavoriteManager.getFavoritesForEndpoint(this.endpoint.guid).pipe( map(f => { From 979d1bb235a08272f5073bb77e2ba514de31e2f5 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 11:40:38 +0000 Subject: [PATCH 16/74] Endpoint card unit test fixes --- .../home-page-endpoint-card.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index 5db24935bd..ecc92fd1c0 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -3,7 +3,7 @@
-
+

{{ endpoint.name }} From 401248870b59150ed552d5dcc24663303f8f3d9f Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 11:57:35 +0000 Subject: [PATCH 17/74] Fix unit tests --- .../home-page-endpoint-card.component.html | 6 +++--- .../home-page-endpoint-card.component.spec.ts | 5 +++++ .../home-page-endpoint-card.component.ts | 8 +------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index ecc92fd1c0..26c3fb5ef6 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -1,9 +1,9 @@ - +
- +
-
+

{{ endpoint.name }} diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts index 30d9db2f0c..b0a7d58566 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts @@ -2,6 +2,7 @@ import { CommonModule } from '@angular/common'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; +import { EndpointModel } from '../../../../../../store/src/types/endpoint.types'; import { createBasicStoreModule } from '../../../../../../store/testing/public-api'; import { CoreTestingModule } from '../../../../../test-framework/core-test.modules'; import { CoreModule, SharedModule } from '../../../../public-api'; @@ -32,6 +33,10 @@ describe('HomePageEndpointCardComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(HomePageEndpointCardComponent); + fixture.componentInstance.endpoint = { + cnsi_type: 'cf', + sub_type: '' + } as EndpointModel; component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index 795fd2b41d..99ee0aa02d 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -104,12 +104,9 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi } ngAfterViewInit() { - if (!this.endpoint) { - return; - } // Dynamically load the component for the Home Card for this endopoint const endpointEntity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type) - if (endpointEntity.definition.homeCard && endpointEntity.definition.homeCard.component) { + if (endpointEntity && endpointEntity.definition.homeCard && endpointEntity.definition.homeCard.component) { this.createCard(endpointEntity); } else { console.warn(`No endpoint home card for ${this.endpoint.guid}`); @@ -117,9 +114,6 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi } ngOnInit() { - if (!this.endpoint) { - return; - } // Favorites for this endpoint this.favorites$ = this.userFavoriteManager.getFavoritesForEndpoint(this.endpoint.guid).pipe( map(f => { From 6d9d94a82344ce31b0a4ff557b2884e66e2ef97f Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 14:02:37 +0000 Subject: [PATCH 18/74] Tidy up favorite card --- .../cfhome-card/cfhome-card.component.html | 2 +- .../home/cfhome-card/cfhome-card.component.ts | 16 ++- .../favorites-meta-card.component.html | 51 +------ .../favorites-meta-card.component.scss | 93 +++---------- .../favorites-meta-card.component.ts | 129 +----------------- .../home-page-endpoint-card.component.html | 6 +- .../home-page-endpoint-card.component.scss | 3 + 7 files changed, 50 insertions(+), 250 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html index 01130f980e..c687bbeb84 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html @@ -17,7 +17,7 @@ - + diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts index 5f5d4ed50d..8426181680 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core'; import { Store } from '@ngrx/store'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; -import { filter, first, map, pairwise, tap } from 'rxjs/operators'; +import { filter, first, map, pairwise } from 'rxjs/operators'; import { PaginationMonitorFactory } from '../../../../../store/src/monitors/pagination-monitor.factory'; import { EndpointModel } from '../../../../../store/src/public-api'; @@ -58,7 +58,8 @@ export class CFHomeCardComponent { routeCount$: Observable; cardLoaded = false; - statsLoaded = false; + + recentApps = []; private appStatsLoaded = new BehaviorSubject(false); private appStatsToLoad: APIResource[] = []; @@ -84,6 +85,7 @@ export class CFHomeCardComponent { // When the apps are loaded, fetch the app stats appsPagObs.entities$.pipe(first()).subscribe(apps => { + this.recentApps = apps; this.appStatsToLoad = this.restrictApps(apps); this.fetchAppStats(); this.fetchAppStats(); @@ -97,18 +99,24 @@ export class CFHomeCardComponent { appsPagObs.entities$, appStatLoaded$ ]).pipe( - map(() => true), - tap(() => { this.statsLoaded = true; }) + map(() => true) ); } public updateLayout() { + const currentRows = this.recentAppsRows; this.recentAppsRows = this.layout.y > 1 ? 5 : 10; // Hide recent apps if more than 2 columns if (this.layout.x > 2) { this.recentAppsRows = 0; } + + // If the layout changes and there are apps to show then we need to fetch the app stats for them + if (this.recentAppsRows > currentRows) { + this.appStatsToLoad = this.restrictApps(this.recentApps); + this.fetchAppStats(); + } } // Fetch the app stats - we fetch two at a time diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html index edbec137cb..cb1e36637a 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html @@ -1,47 +1,10 @@ - - -
-
- {{ icon.icon }} -
-
-

- {{ name$ | async }} -

-
{{ prettyName }}
-
+ +
+ {{ icon.icon }}
- - - -
Favorite not found
- Could - not - fetch {{ prettyName | - lowercase }} - Endpoint - disconnected -
-
Type
-
{{ prettyName }}
-
ID
-
{{ favorite.entityId }}
- +
+
{{ config.name }}
+
{{ prettyName }}
- - - - -
Favorite endpoint is not registered
- - To unfavorite this endpoint, please unfavorite all child entities first. - -
-
ID
-
{{ favorite.endpointId }}
-
-
Disconnected
- +
diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss index e471a073c1..73432811db 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss @@ -1,55 +1,16 @@ +.mat-card.fav-meta-card { + border-radius: 0; + box-shadow: none; +} + .fav-meta-card { + align-items: center; + display: flex; outline: none; - &__header-text { - line-height: 24px; - margin-top: 0; - } - &__content { - height: 100%; - } - &__type { - $type-height: 20px; - font-size: 13px; - line-height: $type-height; - margin-top: -($type-height - 2px); - opacity: .6; - } - &__missing { - align-items: center; - display: flex; - flex-direction: column; - height: 100%; - justify-content: space-around; - padding: 10px; - padding-top: 20px; - &-text { - font-size: 20px; - font-weight: bold; - } - &-small-text { - margin-bottom: 20px; - } - } + padding: 8px 10px; &__clickable { cursor: pointer; } - &__header-text-panel { - width: 100%; - } - &__header { - display: flex; - - } - &__logo-panel { - display: flex; - justify-content: left; - width: 56px; - } - &__logo { - height: 48px; - margin-right: 8px; - width: auto; - } &__icon { margin-right: 4px; opacity: .7; @@ -60,36 +21,20 @@ justify-content: left; width: 32px; } - &__panel { - display: flex; - :first-child { - flex: 1; - } + &__info { + flex: 1; } - &__disconnected { - align-self: center; - border-radius: 2px; - display: flex; + &__name { font-size: 14px; - font-weight: normal; - margin-right: 8px; - padding: 2px 4px; + font-weight: bold; + margin-bottom: 4px; } -} - -.error-details { - display: flex; - flex-direction: column; - padding: 0 20px; - &__value { - padding-bottom: 5px; - } - &__label { - font-size: 10px; + &__type { + font-size: 12px; opacity: .6; } -} - -.subtle-text { - opacity: .6; + &__star { + flex: 0; + opacity: .7; + } } diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts index 2679f73eec..c41abcc8dc 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts @@ -1,24 +1,13 @@ import { Component, Input } from '@angular/core'; -import { isObservable, Observable, of as observableOf } from 'rxjs'; -import { map } from 'rxjs/operators'; import { IFavoritesMetaCardConfig } from '../../../../../../store/src/favorite-config-mapper'; -import { userFavouritesEntityType } from '../../../../../../store/src/helpers/stratos-entity-factory'; -import { entityCatalog, stratosEntityFactory } from '../../../../../../store/src/public-api'; -import { stratosEntityCatalog } from '../../../../../../store/src/stratos-entity-catalog'; -import { MenuItem } from '../../../../../../store/src/types/menu-item.types'; -import { ComponentEntityMonitorConfig, StratosStatus } from '../../../../../../store/src/types/shared.types'; +import { entityCatalog } from '../../../../../../store/src/public-api'; import { IFavoriteEntity } from '../../../../../../store/src/types/user-favorite-manager.types'; import { IFavoriteMetadata, UserFavorite } from '../../../../../../store/src/types/user-favorites.types'; -import { ConfirmationDialogConfig } from '../../../../shared/components/confirmation-dialog.config'; -import { ConfirmationDialogService } from '../../../../shared/components/confirmation-dialog.service'; -import { isEndpointConnected } from '../../../endpoints/connect.service'; interface FavoriteIconData { - hasIcon: boolean; icon?: string; iconFont?: string; - logoUrl?: string; } @Component({ @@ -27,140 +16,32 @@ interface FavoriteIconData { styleUrls: ['./favorites-meta-card.component.scss'] }) export class FavoritesMetaCardComponent { - @Input() - public compact = false; - - @Input() - public placeholder = false; - - @Input() - public endpoint = false; - - @Input() - public endpointHasEntities = false; - - @Input() - public endpointDisconnected = false; @Input() - public mode: string; - - public config: IFavoritesMetaCardConfig; - - public status$: Observable; + public endpoint; public favorite: UserFavorite; - /* - We use this to pass the favorite to the metacard, this dictates if we should show the favorite star or not. - We do not want to show the favorite star for endpoints that have favorite entities. - */ - public metaFavorite: UserFavorite; - - public entityConfig: ComponentEntityMonitorConfig; - - public showMore: boolean; - public prettyName: string; - public confirmation: ConfirmationDialogConfig; - - public endpointConnected$: Observable; - public name$: Observable; - public routerLink$: Observable; - public actions$: Observable; - - // Optional icon for the favorite - public iconUrl$: Observable; + public config: IFavoritesMetaCardConfig; - // Optional icon for the favorite public icon: FavoriteIconData; @Input() set favoriteEntity(favoriteEntity: IFavoriteEntity) { - if (!this.placeholder && favoriteEntity) { - const endpoint$ = stratosEntityCatalog.endpoint.store.getEntityMonitor(favoriteEntity.favorite.endpointId).entity$; - this.endpointConnected$ = endpoint$.pipe(map(endpoint => isEndpointConnected(endpoint))); - this.actions$ = this.endpointConnected$.pipe( - map(connected => connected ? this.config.menuItems : []) - ); + if (favoriteEntity) { const { cardMapper, favorite, prettyName } = favoriteEntity; this.favorite = favorite; - this.metaFavorite = !this.endpoint || (this.endpoint && !this.endpointHasEntities) ? favorite : null; this.prettyName = prettyName || 'Unknown'; - this.entityConfig = new ComponentEntityMonitorConfig(favorite.guid, stratosEntityFactory(userFavouritesEntityType)); - - // If this favorite is an endpoint, lookup the image for it from the entity catalog - if (this.favorite.entityType === 'endpoint') { - this.iconUrl$ = endpoint$.pipe(map(a => entityCatalog.getEndpoint(a.cnsi_type, a.sub_type).definition.logoUrl)); - } else { - this.iconUrl$ = observableOf(''); - } - const entityDef = entityCatalog.getEntity(this.favorite.endpointType, this.favorite.entityType); this.icon = { - hasIcon: !!entityDef.definition.logoUrl || !!entityDef.definition.icon, icon: entityDef.definition.icon, iconFont: entityDef.definition.iconFont, - logoUrl: entityDef.definition.logoUrl, }; - this.setConfirmation(this.prettyName, favorite); - const config = cardMapper && favorite && favorite.metadata ? cardMapper(favorite.metadata) : null; - - if (config) { - this.name$ = observableOf(config.name); - if (this.endpoint) { - this.routerLink$ = this.endpointConnected$.pipe(map(connected => connected ? config.routerLink : '/endpoints')); - } else { - this.routerLink$ = this.endpointConnected$.pipe(map(connected => connected ? config.routerLink : null)); - } - config.lines = this.mapLinesToObservables(config.lines); - this.config = config; - } + this.config = config; } } - - constructor( - private confirmDialog: ConfirmationDialogService - ) { } - - public setConfirmation(prettyName: string, favorite: UserFavorite) { - this.confirmation = new ConfirmationDialogConfig( - `Unfavorite ${prettyName}`, - `Are you sure you would like to unfavorite this ${prettyName.toLocaleLowerCase()} with the id ${favorite.entityId}?`, - 'Yes', - true - ); - } - - public confirmFavoriteRemoval() { - this.confirmDialog.open(this.confirmation, this.removeFavorite); - } - - private removeFavorite = () => { - stratosEntityCatalog.userFavorite.api.delete(this.favorite); - }; - - public toggleMoreError() { - this.showMore = !this.showMore; - } - - private mapLinesToObservables(lines: [string, string | Observable][]) { - if (!lines) { - return []; - } - return lines.map(line => { - const [label, value] = line; - if (!isObservable(value)) { - return [ - label, - observableOf(value) - ] as [string, Observable]; - } - return line; - }); - } - } diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index 26c3fb5ef6..7dce3a1b7b 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -1,11 +1,11 @@ - +

-
+
{{ endpoint.name }}

@@ -31,7 +31,7 @@

-
+
Favorites
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss index f1e49428d3..f9a6ea4f47 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss @@ -138,6 +138,9 @@ box-shadow: none; } } + &__right-2 { + min-width: 280px; + } &__fav { > * { margin-bottom: 10px; From 36915fa77a59fca813cb2c833f7a5940c5992185 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 14:32:45 +0000 Subject: [PATCH 19/74] Further refinement to the UI --- .../home/cfhome-card/cfhome-card.component.html | 8 +++++++- .../home/cfhome-card/cfhome-card.component.scss | 16 ++++++++++++++++ .../home/cfhome-card/cfhome-card.component.ts | 7 +++++-- .../card-cf-recent-apps.component.html | 2 +- .../card-cf-recent-apps.component.ts | 11 ++++++++++- 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html index c687bbeb84..097af1d951 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html @@ -17,8 +17,14 @@ - + + +
+ apps +
You don't have any applications
+ +
diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss index 6e6b1569ce..d4d45ce4a2 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss @@ -4,4 +4,20 @@ margin-right: 1em; margin-top: 1em; } + &__no-apps { + align-items: center; + display: flex; + flex-direction: column; + margin-top: 20px; + opacity: 0.7; + > mat-icon { + font-size: 40px; + height: 40px; + width: 40px; + } + > div { + font-size: 16px; + margin-top: 10px; + } + } } diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts index 8426181680..5ee029853b 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts @@ -57,6 +57,8 @@ export class CFHomeCardComponent { orgCount$: Observable; routeCount$: Observable; + hasNoApps$: Observable; + cardLoaded = false; recentApps = []; @@ -84,12 +86,13 @@ export class CFHomeCardComponent { const appsPagObs = cfEntityCatalog.application.store.getPaginationService(this.guid); // When the apps are loaded, fetch the app stats - appsPagObs.entities$.pipe(first()).subscribe(apps => { + this.hasNoApps$ = appsPagObs.entities$.pipe(first(), map(apps => { this.recentApps = apps; this.appStatsToLoad = this.restrictApps(apps); this.fetchAppStats(); this.fetchAppStats(); - }); + return apps.length === 0; + })); const appStatLoaded$ = this.appStatsLoaded.asObservable().pipe(filter(loaded => loaded)); return combineLatest([ diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.html b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.html index 5ca35c0a37..7c77d6d64c 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.html +++ b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.html @@ -1,5 +1,5 @@ - + Recently updated applications >; hasEntities$: Observable; + show$: Observable; private maxRowsSubject = new BehaviorSubject(RECENT_ITEMS_COUNT); @@ -71,6 +73,13 @@ export class CardCfRecentAppsComponent implements OnInit { map(([apps, maxRows]) => this.restrictApps(apps, maxRows)), tap(apps => this.fetchAppStats(apps)) ); + + this.show$ = this.allApps$.pipe( + map(apps => { + return !this.hideWhenEmpty || this.hideWhenEmpty && apps.length > 0; + }), + startWith(true), + ); } private fetchAppStats(recentApps: APIResource[]) { From 67c0a1c31e10d875d47f40132fc00ee9d2a7244c Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 14:46:17 +0000 Subject: [PATCH 20/74] Fix missing dates from recents on CF view --- .../src/features/home/cfhome-card/cfhome-card.component.scss | 1 + .../cards/card-cf-recent-apps/card-cf-recent-apps.component.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss index d4d45ce4a2..23bfb70f68 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss @@ -18,6 +18,7 @@ > div { font-size: 16px; margin-top: 10px; + text-align: center; } } } diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.ts b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.ts index d231897af1..fc4a2128da 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.ts @@ -23,7 +23,7 @@ export class CardCfRecentAppsComponent implements OnInit { @Output() refresh = new EventEmitter(); @Input() endpoint: string; @Input() mode: string; - @Input() showDate: boolean; + @Input() showDate = true; @Input() dateMode: string; @Input() noStats = false; @Input() placeholderMode = false; From a28fa611cfcf649a20d5b3fefafd9584e2c2a6f6 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 15:27:13 +0000 Subject: [PATCH 21/74] Get app deploy from home screen working --- .../deploy-application.component.ts | 6 ++++++ .../new-application-base-step.component.ts | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/deploy-application/deploy-application.component.ts b/src/frontend/packages/cloud-foundry/src/features/applications/deploy-application/deploy-application.component.ts index ff481bc25d..7dd21b8781 100644 --- a/src/frontend/packages/cloud-foundry/src/features/applications/deploy-application/deploy-application.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/applications/deploy-application/deploy-application.component.ts @@ -22,6 +22,7 @@ import { RouterNav } from '../../../../../store/src/actions/router.actions'; import { selectPaginationState } from '../../../../../store/src/selectors/pagination.selectors'; import { CfAppsDataSource } from '../../../shared/components/list/list-types/app/cf-apps-data-source'; import { CfOrgSpaceDataService } from '../../../shared/data-services/cf-org-space-service.service'; +import { AUTO_SELECT_CF_URL_PARAM } from '../new-application-base-step/new-application-base-step.component'; import { ApplicationDeploySourceTypes } from './deploy-application-steps.types'; @Component({ @@ -78,6 +79,11 @@ export class DeployApplicationComponent implements OnInit, OnDestroy { } ngOnInit(): void { + // Has the endpoint ID been specified in the URL? + const endpoint = this.activatedRoute.snapshot.queryParams[AUTO_SELECT_CF_URL_PARAM]; + if (endpoint) { + this.cfOrgSpaceService.cf.select.next(endpoint); + } if (this.appGuid) { this.deployButtonText = 'Redeploy'; diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/new-application-base-step/new-application-base-step.component.ts b/src/frontend/packages/cloud-foundry/src/features/applications/new-application-base-step/new-application-base-step.component.ts index 24b9f15696..3b020fc397 100644 --- a/src/frontend/packages/cloud-foundry/src/features/applications/new-application-base-step/new-application-base-step.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/applications/new-application-base-step/new-application-base-step.component.ts @@ -1,4 +1,5 @@ import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; import { Store } from '@ngrx/store'; import { CFAppState } from '../../../../../cloud-foundry/src/cf-app-state'; @@ -11,6 +12,9 @@ import { AUTO_SELECT_DEPLOY_TYPE_URL_PARAM, } from '../deploy-application/deploy-application-steps.types'; +export const AUTO_SELECT_CF_URL_PARAM = 'auto-select-endpoint'; + + interface IAppTileData extends ITileData { type: string; subType?: string; @@ -36,6 +40,12 @@ export class NewApplicationBaseStepComponent { if (tile.data.subType) { query[AUTO_SELECT_DEPLOY_TYPE_URL_PARAM] = tile.data.subType; } + const endpoint = this.activatedRoute.snapshot.params.endpointId; + if (endpoint) { + query[AUTO_SELECT_CF_URL_PARAM] = endpoint; + query[BASE_REDIRECT_QUERY] += `/${endpoint}`; + } + this.store.dispatch(new RouterNav({ path: `${baseUrl}/${type}`, query @@ -45,7 +55,10 @@ export class NewApplicationBaseStepComponent { constructor( private store: Store, - appDeploySourceTypes: ApplicationDeploySourceTypes) { + appDeploySourceTypes: ApplicationDeploySourceTypes, + private activatedRoute: ActivatedRoute, + + ) { this.sourceTypes = appDeploySourceTypes.getTypes(); this.tileSelectorConfig = [ ...this.sourceTypes.map(type => From 869d2702343711d3aefd3a28535d26517a97ec60 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 6 Nov 2020 17:01:18 +0000 Subject: [PATCH 22/74] Add message for when no connected endpoints --- .../src/features/home/home/home-page.component.html | 10 ++++++++-- .../src/features/home/home/home-page.component.ts | 11 +++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.html b/src/frontend/packages/core/src/features/home/home/home-page.component.html index c25e4b388e..9914c06651 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.html @@ -18,7 +18,7 @@

Home

-
+
@@ -26,5 +26,11 @@

Home

+
+ +
+
+ + - \ No newline at end of file + diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.ts index 922ba5db24..6a5e368311 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.ts @@ -43,6 +43,8 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { private layout = new BehaviorSubject(null); public layout$: Observable; + public haveThingsToShow$: Observable; + public columns = 1; public layoutID = 0; @@ -99,8 +101,10 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { ); this.haveRegistered$ = this.endpointsService.haveRegistered$; + const connected$ = this.endpointsService.endpoints$; + // Only show endpoints that have Home Card metadata - this.endpoints$ = combineLatest([this.endpointsService.endpoints$, userFavoriteManager.getAllFavorites()]).pipe( + this.endpoints$ = combineLatest([connected$, userFavoriteManager.getAllFavorites()]).pipe( map(([endpoints, [favGroups, favs]]) => { const ordered = this.orderEndpoints(endpoints, favGroups); return ordered.filter(ep => { @@ -110,6 +114,8 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { }) ); + this.haveThingsToShow$ = this.endpoints$.pipe(map(eps => eps.length > 0), startWith(true)); + // Set an initial layout this.layout.next(this.getLayout(1, 1)); @@ -261,7 +267,8 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { } }) - return result; + // Filter out the disconnected ones + return result.filter(ep => ep.connectionStatus === 'connected'); } // Automatic layout - select the best layout based on the available endpoints From e97501844d4ea16e5c0ba8abfbc4926f9d94934c Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Sat, 7 Nov 2020 10:45:20 +0000 Subject: [PATCH 23/74] Unit test fixes --- .../cfhome-card/cfhome-card.component.scss | 2 +- .../favorites-side-panel.component.html | 2 +- .../home-page-endpoint-card.component.html | 3 +-- .../home-page-endpoint-card.component.spec.ts | 7 +++-- .../home-page-endpoint-card.component.ts | 27 ++++++++++--------- .../kubernetes-home-card.component.spec.ts | 14 +++++----- 6 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss index 23bfb70f68..5f071e3a00 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss @@ -17,7 +17,7 @@ } > div { font-size: 16px; - margin-top: 10px; + margin: 10px 8px; text-align: center; } } diff --git a/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.html b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.html index 76f906bf15..9cdf6ea603 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.html +++ b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.html @@ -1,7 +1,7 @@
- +
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index 7dce3a1b7b..a992812f79 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -38,8 +38,7 @@

- +
No favorites
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts index b0a7d58566..eb75b1541c 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts @@ -34,13 +34,16 @@ describe('HomePageEndpointCardComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(HomePageEndpointCardComponent); fixture.componentInstance.endpoint = { - cnsi_type: 'cf', - sub_type: '' + cnsi_type: 'metrics', } as EndpointModel; component = fixture.componentInstance; fixture.detectChanges(); }); + afterEach(() => { + component.ngOnDestroy(); + }) + it('should create', () => { expect(component).toBeTruthy(); }); diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index 99ee0aa02d..627190ba82 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -122,25 +122,28 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi ); this.entity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type) - this.definition = this.entity.definition; - this.favorite = this.favoritesConfigMapper.getFavoriteEndpointFromEntity(this.endpoint); + if (this.entity) { + this.definition = this.entity.definition; + this.favorite = this.favoritesConfigMapper.getFavoriteEndpointFromEntity(this.endpoint); - // Get the list of shortcuts for the endpoint for the given endpoint ID - if (this.definition.homeCard && this.definition.homeCard.shortcuts) { - this.shortcuts = this.definition.homeCard.shortcuts(this.endpoint.guid); - } + // Get the list of shortcuts for the endpoint for the given endpoint ID + if (this.definition.homeCard && this.definition.homeCard.shortcuts) { + this.shortcuts = this.definition.homeCard.shortcuts(this.endpoint.guid); + } - this.fullView = this.definition.homeCard && this.definition.homeCard.fullView; + this.fullView = this.definition.homeCard && this.definition.homeCard.fullView; - const mapper = this.favoritesConfigMapper.getMapperFunction(this.favorite); - if (mapper && this.favorite.metadata) { - const p = mapper(this.favorite.metadata); - if (p) { - this.link = p.routerLink; + const mapper = this.favoritesConfigMapper.getMapperFunction(this.favorite); + if (mapper && this.favorite.metadata) { + const p = mapper(this.favorite.metadata); + if (p) { + this.link = p.routerLink; + } } } this.links$ = combineLatest([this.favorites$, this.layout$.asObservable()]).pipe( + filter(([favs, layout]) => !!layout), map(([favs, layout]) => { let shortcuts: HomeCardShortcut[] = this.shortcuts || []; diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.spec.ts b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.spec.ts index 4cf6c96f76..372e3bcf9e 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.spec.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.spec.ts @@ -1,20 +1,22 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { CFHomeCardComponent } from './kubernetes-home-card.component'; +import { KubernetesEndpointService } from '../services/kubernetes-endpoint.service'; +import { KubernetesHomeCardComponent } from './kubernetes-home-card.component'; -describe('CFHomeCardComponent', () => { - let component: CFHomeCardComponent; - let fixture: ComponentFixture; +describe('KubernetesHomeCardComponent', () => { + let component: KubernetesHomeCardComponent; + let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ CFHomeCardComponent ] + declarations: [ KubernetesHomeCardComponent ], + providers: [ KubernetesEndpointService ] }) .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(CFHomeCardComponent); + fixture = TestBed.createComponent(KubernetesHomeCardComponent); component = fixture.componentInstance; fixture.detectChanges(); }); From 72fb252d3926ccc9bcdd5dc78a0ffb9eb50425be Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Sat, 7 Nov 2020 11:17:52 +0000 Subject: [PATCH 24/74] Kubernetes Home Card unit test fixes --- .../kubernetes/home/kubernetes-home-card.component.spec.ts | 7 ++++++- .../src/kubernetes/home/kubernetes-home-card.component.ts | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.spec.ts b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.spec.ts index 372e3bcf9e..392a169015 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.spec.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.spec.ts @@ -1,5 +1,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { EndpointModel } from '../../../../store/src/types/endpoint.types'; +import { BaseKubeGuid } from '../kubernetes-page.types'; +import { KubernetesBaseTestModules } from '../kubernetes.testing.module'; import { KubernetesEndpointService } from '../services/kubernetes-endpoint.service'; import { KubernetesHomeCardComponent } from './kubernetes-home-card.component'; @@ -10,7 +13,8 @@ describe('KubernetesHomeCardComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ KubernetesHomeCardComponent ], - providers: [ KubernetesEndpointService ] + imports: [...KubernetesBaseTestModules], + providers: [ KubernetesEndpointService, BaseKubeGuid ] }) .compileComponents(); })); @@ -18,6 +22,7 @@ describe('KubernetesHomeCardComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(KubernetesHomeCardComponent); component = fixture.componentInstance; + component.endpoint = {} as EndpointModel; fixture.detectChanges(); }); diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts index 0c644ab43b..2ade3752e4 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts @@ -40,7 +40,9 @@ export class KubernetesHomeCardComponent implements OnInit { ngOnInit() { const guid = this.endpoint.guid; - this.kubeEndpointService.initialize(this.endpoint.guid); + if (guid) { + this.kubeEndpointService.initialize(this.endpoint.guid); + } this.shortcuts = [ { From 5d2a5b5ae784e01db46385efbb18c7b70bb342a9 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Sat, 7 Nov 2020 11:30:46 +0000 Subject: [PATCH 25/74] Fix unit test error --- .../core/src/features/home/home/home-page.component.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.spec.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.spec.ts index 9579ad3f19..50bc61bf43 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.spec.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.spec.ts @@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; -import { createBasicStoreModule } from '@stratosui/store/testing'; +import { createEmptyStoreModule } from '@stratosui/store/testing'; import { CoreTestingModule } from '../../../../test-framework/core-test.modules'; import { CoreModule } from '../../../core/core.module'; @@ -25,7 +25,7 @@ describe('HomePageComponent', () => { RouterTestingModule, NoopAnimationsModule, CoreTestingModule, - createBasicStoreModule() + createEmptyStoreModule() ], providers: [ TabNavService, From 2d4627b265655757787033d74ebd564c67f48363 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Sat, 7 Nov 2020 18:48:09 +0000 Subject: [PATCH 26/74] Move components to remove dependency on large shared module --- .../src/features/cf/cloud-foundry-section.module.ts | 4 +++- .../card-cf-recent-apps.component.html | 0 .../card-cf-recent-apps.component.scss | 0 .../card-cf-recent-apps.component.spec.ts | 0 .../card-cf-recent-apps.component.ts | 11 ++++++----- .../compact-app-card/compact-app-card.component.html | 0 .../compact-app-card/compact-app-card.component.scss | 0 .../compact-app-card.component.spec.ts | 0 .../compact-app-card/compact-app-card.component.ts | 12 ++++++------ .../features/home/cfhome-card/cfhome-card.module.ts | 12 ++++++++++-- .../cloud-foundry/src/shared/cf-shared.module.ts | 6 ------ 11 files changed, 25 insertions(+), 20 deletions(-) rename src/frontend/packages/cloud-foundry/src/{shared/components/cards => features/home}/card-cf-recent-apps/card-cf-recent-apps.component.html (100%) rename src/frontend/packages/cloud-foundry/src/{shared/components/cards => features/home}/card-cf-recent-apps/card-cf-recent-apps.component.scss (100%) rename src/frontend/packages/cloud-foundry/src/{shared/components/cards => features/home}/card-cf-recent-apps/card-cf-recent-apps.component.spec.ts (100%) rename src/frontend/packages/cloud-foundry/src/{shared/components/cards => features/home}/card-cf-recent-apps/card-cf-recent-apps.component.ts (87%) rename src/frontend/packages/cloud-foundry/src/{shared/components/cards => features/home}/card-cf-recent-apps/compact-app-card/compact-app-card.component.html (100%) rename src/frontend/packages/cloud-foundry/src/{shared/components/cards => features/home}/card-cf-recent-apps/compact-app-card/compact-app-card.component.scss (100%) rename src/frontend/packages/cloud-foundry/src/{shared/components/cards => features/home}/card-cf-recent-apps/compact-app-card/compact-app-card.component.spec.ts (100%) rename src/frontend/packages/cloud-foundry/src/{shared/components/cards => features/home}/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts (78%) diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/cloud-foundry-section.module.ts b/src/frontend/packages/cloud-foundry/src/features/cf/cloud-foundry-section.module.ts index 688e900238..f8adc26ba8 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/cloud-foundry-section.module.ts +++ b/src/frontend/packages/cloud-foundry/src/features/cf/cloud-foundry-section.module.ts @@ -10,6 +10,7 @@ import { CloudFoundrySharedModule } from '../../shared/cf-shared.module'; import { CFEndpointsListConfigService, } from '../../shared/components/list/list-types/cf-endpoints/cf-endpoints-list-config.service'; +import { CFHomeCardModule } from '../home/cfhome-card/cfhome-card.module'; import { AddOrganizationComponent } from './add-organization/add-organization.component'; import { CreateOrganizationStepComponent, @@ -138,7 +139,8 @@ import { RemoveUserComponent } from './users/remove-user/remove-user.component'; CloudFoundrySectionRoutingModule, RouterModule, NgxChartsModule, - CloudFoundrySharedModule + CloudFoundrySharedModule, + CFHomeCardModule, ], declarations: [ CloudFoundryBaseComponent, diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.html b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.html similarity index 100% rename from src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.html rename to src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.html diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.scss b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.scss similarity index 100% rename from src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.scss rename to src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.scss diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.spec.ts b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.spec.ts similarity index 100% rename from src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.spec.ts rename to src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.spec.ts diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.ts similarity index 87% rename from src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.ts rename to src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.ts index fc4a2128da..bd5bfa11db 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.ts @@ -2,11 +2,12 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; import { filter, map, startWith, tap } from 'rxjs/operators'; -import { PaginationObservables } from '../../../../../../store/src/reducers/pagination-reducer/pagination-reducer.types'; -import { APIResource } from '../../../../../../store/src/types/api.types'; -import { IApp } from '../../../../cf-api.types'; -import { cfEntityCatalog } from '../../../../cf-entity-catalog'; -import { appDataSort } from '../../../../features/cf/services/cloud-foundry-endpoint.service'; +import { PaginationObservables } from '../../../../../store/src/reducers/pagination-reducer/pagination-reducer.types'; +import { APIResource } from '../../../../../store/src/types/api.types'; +import { IApp } from '../../../cf-api.types'; +import { cfEntityCatalog } from '../../../cf-entity-catalog'; +import { appDataSort } from '../../cf/services/cloud-foundry-endpoint.service'; + const RECENT_ITEMS_COUNT = 10; diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.html b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.html similarity index 100% rename from src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.html rename to src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.html diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.scss b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.scss similarity index 100% rename from src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.scss rename to src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.scss diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.spec.ts b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.spec.ts similarity index 100% rename from src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.spec.ts rename to src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.spec.ts diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts similarity index 78% rename from src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts rename to src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts index 43244cdc49..6e3aef7245 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts @@ -3,12 +3,12 @@ import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; -import { CFAppState } from '../../../../../../../cloud-foundry/src/cf-app-state'; -import { ApplicationService } from '../../../../../../../cloud-foundry/src/features/applications/application.service'; -import { BREADCRUMB_URL_PARAM } from '../../../../../../../core/src/shared/components/breadcrumbs/breadcrumbs.types'; -import { StratosStatus } from '../../../../../../../store/src/types/shared.types'; -import { ActiveRouteCfOrgSpace } from '../../../../../features/cf/cf-page.types'; -import { ApplicationStateData, ApplicationStateService } from '../../../../services/application-state.service'; +import { BREADCRUMB_URL_PARAM } from '../../../../../../core/src/shared/components/breadcrumbs/breadcrumbs.types'; +import { StratosStatus } from '../../../../../../store/src/types/shared.types'; +import { CFAppState } from '../../../../cf-app-state'; +import { ApplicationStateData, ApplicationStateService } from '../../../../shared/services/application-state.service'; +import { ApplicationService } from '../../../applications/application.service'; +import { ActiveRouteCfOrgSpace } from '../../../cf/cf-page.types'; @Component({ diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.module.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.module.ts index 5ebd521e04..03ef6071ff 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.module.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.module.ts @@ -4,7 +4,9 @@ import { RouterModule } from '@angular/router'; import { CoreModule } from '../../../../../core/src/core/core.module'; import { HomeModule } from '../../../../../core/src/features/home/home.module'; import { SharedModule } from '../../../../../core/src/public-api'; -import { CloudFoundrySharedModule } from '../../../shared/cf-shared.module'; +import { ApplicationStateService } from '../../../shared/services/application-state.service'; +import { CardCfRecentAppsComponent } from '../card-cf-recent-apps/card-cf-recent-apps.component'; +import { CompactAppCardComponent } from '../card-cf-recent-apps/compact-app-card/compact-app-card.component'; import { MDAppModule } from './../../../../../core/src/core/md.module'; import { CFHomeCardComponent } from './cfhome-card.component'; @@ -14,15 +16,21 @@ import { CFHomeCardComponent } from './cfhome-card.component'; RouterModule, MDAppModule, SharedModule, - CloudFoundrySharedModule, HomeModule, ], declarations: [ CFHomeCardComponent, + CardCfRecentAppsComponent, + CompactAppCardComponent, ], exports: [ CFHomeCardComponent, + CardCfRecentAppsComponent, + CompactAppCardComponent, ], + providers: [ + ApplicationStateService, + ] }) export class CFHomeCardModule { diff --git a/src/frontend/packages/cloud-foundry/src/shared/cf-shared.module.ts b/src/frontend/packages/cloud-foundry/src/shared/cf-shared.module.ts index 206abb267a..46ec970398 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/cf-shared.module.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/cf-shared.module.ts @@ -31,8 +31,6 @@ import { CardCfInfoComponent } from './components/cards/card-cf-info/card-cf-inf import { CardCfOrgUserDetailsComponent, } from './components/cards/card-cf-org-user-details/card-cf-org-user-details.component'; -import { CardCfRecentAppsComponent } from './components/cards/card-cf-recent-apps/card-cf-recent-apps.component'; -import { CompactAppCardComponent } from './components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component'; import { CardCfSpaceDetailsComponent } from './components/cards/card-cf-space-details/card-cf-space-details.component'; import { CardCfUserInfoComponent } from './components/cards/card-cf-user-info/card-cf-user-info.component'; import { @@ -300,8 +298,6 @@ const cfListCards: Type>[] = [ CardCfUserInfoComponent, CardCfOrgUserDetailsComponent, CardCfSpaceDetailsComponent, - CardCfRecentAppsComponent, - CompactAppCardComponent, ServiceSummaryCardComponent, ServiceBrokerCardComponent, ServiceRecentInstancesCardComponent, @@ -349,8 +345,6 @@ const cfListCards: Type>[] = [ CardCfUserInfoComponent, CardCfOrgUserDetailsComponent, CardCfSpaceDetailsComponent, - CardCfRecentAppsComponent, - CompactAppCardComponent, ServiceSummaryCardComponent, ServiceBrokerCardComponent, ServiceRecentInstancesCardComponent, From 1bb7d8aecc3e9e57352bb3b9cab59ff205e9bb87 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Sat, 7 Nov 2020 21:59:11 +0000 Subject: [PATCH 27/74] Fix unit test and add deploy tiles to CF Home Card --- .../new-application-base-step.component.ts | 3 +- .../card-cf-recent-apps.component.spec.ts | 16 +++---- .../compact-app-card.component.spec.ts | 10 ++-- .../cfhome-card/cfhome-card.component.html | 7 ++- .../cfhome-card/cfhome-card.component.scss | 14 ++++-- .../home/cfhome-card/cfhome-card.component.ts | 48 +++++++++++++++++++ .../home/cfhome-card/cfhome-card.module.ts | 2 + .../tile-selector-tile.component.html | 2 +- .../tile-selector-tile.component.scss | 10 ++++ .../tile-selector-tile.component.ts | 2 + .../tile-selector.component.html | 2 +- .../tile-selector/tile-selector.component.ts | 1 + 12 files changed, 95 insertions(+), 22 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/new-application-base-step/new-application-base-step.component.ts b/src/frontend/packages/cloud-foundry/src/features/applications/new-application-base-step/new-application-base-step.component.ts index 3b020fc397..66d77a7be0 100644 --- a/src/frontend/packages/cloud-foundry/src/features/applications/new-application-base-step/new-application-base-step.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/applications/new-application-base-step/new-application-base-step.component.ts @@ -15,10 +15,11 @@ import { export const AUTO_SELECT_CF_URL_PARAM = 'auto-select-endpoint'; -interface IAppTileData extends ITileData { +export interface IAppTileData extends ITileData { type: string; subType?: string; } + @Component({ selector: 'app-new-application-base-step', templateUrl: './new-application-base-step.component.html', diff --git a/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.spec.ts b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.spec.ts index b93ba4295a..53fbd7045f 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.spec.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.spec.ts @@ -3,21 +3,21 @@ import { of as observableOf } from 'rxjs'; import { ApplicationStateIconComponent, -} from '../../../../../../core/src/shared/components/application-state/application-state-icon/application-state-icon.component'; +} from '../../../../../core/src/shared/components/application-state/application-state-icon/application-state-icon.component'; import { ApplicationStateIconPipe, -} from '../../../../../../core/src/shared/components/application-state/application-state-icon/application-state-icon.pipe'; +} from '../../../../../core/src/shared/components/application-state/application-state-icon/application-state-icon.pipe'; import { PollingIndicatorComponent, -} from '../../../../../../core/src/shared/components/polling-indicator/polling-indicator.component'; -import { EntityMonitorFactory } from '../../../../../../store/src/monitors/entity-monitor.factory.service'; -import { PaginationMonitorFactory } from '../../../../../../store/src/monitors/pagination-monitor.factory'; +} from '../../../../../core/src/shared/components/polling-indicator/polling-indicator.component'; +import { EntityMonitorFactory } from '../../../../../store/src/monitors/entity-monitor.factory.service'; +import { PaginationMonitorFactory } from '../../../../../store/src/monitors/pagination-monitor.factory'; import { generateActiveRouteCfOrgSpaceMock, generateCfBaseTestModulesNoShared, -} from '../../../../../test-framework/cloud-foundry-endpoint-service.helper'; -import { CloudFoundryEndpointService } from '../../../../features/cf/services/cloud-foundry-endpoint.service'; -import { CfUserService } from '../../../data-services/cf-user.service'; +} from '../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { CfUserService } from '../../../shared/data-services/cf-user.service'; +import { CloudFoundryEndpointService } from '../../cf/services/cloud-foundry-endpoint.service'; import { CardCfRecentAppsComponent } from './card-cf-recent-apps.component'; import { CompactAppCardComponent } from './compact-app-card/compact-app-card.component'; diff --git a/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.spec.ts b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.spec.ts index d6655a60b1..2f7bc155e6 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.spec.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.spec.ts @@ -2,13 +2,13 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ApplicationStateIconComponent, -} from '../../../../../../../core/src/shared/components/application-state/application-state-icon/application-state-icon.component'; +} from '../../../../../../core/src/shared/components/application-state/application-state-icon/application-state-icon.component'; import { ApplicationStateIconPipe, -} from '../../../../../../../core/src/shared/components/application-state/application-state-icon/application-state-icon.pipe'; -import { generateCfBaseTestModulesNoShared } from '../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; -import { ActiveRouteCfOrgSpace } from '../../../../../features/cf/cf-page.types'; -import { ApplicationStateService } from '../../../../services/application-state.service'; +} from '../../../../../../core/src/shared/components/application-state/application-state-icon/application-state-icon.pipe'; +import { generateCfBaseTestModulesNoShared } from '../../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { ApplicationStateService } from '../../../../shared/services/application-state.service'; +import { ActiveRouteCfOrgSpace } from '../../../cf/cf-page.types'; import { CompactAppCardComponent } from './compact-app-card.component'; describe('CompactAppCardComponent', () => { diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html index 097af1d951..4fe72cddd9 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.html @@ -22,8 +22,11 @@
apps -
You don't have any applications
- +
You don't have any applications
+
+
Get started by deploying an Application from ...
+ +
diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss index 5f071e3a00..85cb520946 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.scss @@ -11,14 +11,20 @@ margin-top: 20px; opacity: 0.7; > mat-icon { - font-size: 40px; - height: 40px; - width: 40px; + font-size: 48px; + height: 48px; + width: 48px; } - > div { + &-title { font-size: 16px; margin: 10px 8px; text-align: center; } + &-deploy { + font-size: 16px; + font-weight: bold; + margin: 20px 0; + text-align: center; + } } } diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts index 5ee029853b..e4a95dbc6f 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts @@ -3,6 +3,8 @@ import { Store } from '@ngrx/store'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { filter, first, map, pairwise } from 'rxjs/operators'; +import { BASE_REDIRECT_QUERY } from '../../../../../core/src/shared/components/stepper/stepper.types'; +import { RouterNav } from '../../../../../store/src/actions/router.actions'; import { PaginationMonitorFactory } from '../../../../../store/src/monitors/pagination-monitor.factory'; import { EndpointModel } from '../../../../../store/src/public-api'; import { ActionState } from '../../../../../store/src/reducers/api-request-reducer/types'; @@ -10,10 +12,20 @@ import { APIResource } from '../../../../../store/src/types/api.types'; import { IApp } from '../../../cf-api.types'; import { CFAppState } from '../../../cf-app-state'; import { cfEntityCatalog } from '../../../cf-entity-catalog'; +import { SourceType } from '../../../store/types/deploy-application.types'; +import { + ApplicationDeploySourceTypes, + AUTO_SELECT_DEPLOY_TYPE_URL_PARAM, +} from '../../applications/deploy-application/deploy-application-steps.types'; +import { + AUTO_SELECT_CF_URL_PARAM, + IAppTileData, +} from '../../applications/new-application-base-step/new-application-base-step.component'; import { ActiveRouteCfOrgSpace } from '../../cf/cf-page.types'; import { goToAppWall } from '../../cf/cf.helpers'; import { appDataSort, CloudFoundryEndpointService } from '../../cf/services/cloud-foundry-endpoint.service'; import { HomePageCardLayout } from './../../../../../core/src/features/home/home.types'; +import { ITileConfig } from './../../../../../core/src/shared/components/tile/tile-selector.types'; @Component({ @@ -66,12 +78,45 @@ export class CFHomeCardComponent { private appStatsLoaded = new BehaviorSubject(false); private appStatsToLoad: APIResource[] = []; + private sourceTypes: SourceType[]; + public tileSelectorConfig: ITileConfig[]; + + showDeployAppTiles = false; + constructor( private store: Store, private pmf: PaginationMonitorFactory, + appDeploySourceTypes: ApplicationDeploySourceTypes, ) { // Set a default layout this._layout = new HomePageCardLayout(1, 1); + + // Get source types for if we are showing tiles to deploy an application + this.sourceTypes = appDeploySourceTypes.getTypes(); + this.tileSelectorConfig = [ + ...this.sourceTypes.map(type => + new ITileConfig( + type.name, + type.graphic, + { type: 'deploy', subType: type.id }, + ) + ) + ]; + } + + // Deploy an app from the Home Card for the given endpoint + set selectedTile(tile: ITileConfig) { + const type = tile ? tile.data.type : null; + if (tile) { + const query = { + [BASE_REDIRECT_QUERY]: `applications/new/${this.guid}`, + [AUTO_SELECT_CF_URL_PARAM]:this.guid + }; + if (tile.data.subType) { + query[AUTO_SELECT_DEPLOY_TYPE_URL_PARAM] = tile.data.subType; + } + this.store.dispatch(new RouterNav({ path: `applications/${type}`, query })); + } } // Card is instructed to load its view by the container, whn it is visible @@ -120,6 +165,9 @@ export class CFHomeCardComponent { this.appStatsToLoad = this.restrictApps(this.recentApps); this.fetchAppStats(); } + + // Only show the deploy app tiles in the full view + this.showDeployAppTiles = this.layout.x === 1 && this.layout.y === 1; } // Fetch the app stats - we fetch two at a time diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.module.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.module.ts index 03ef6071ff..c9bd1f1179 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.module.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.module.ts @@ -5,6 +5,7 @@ import { CoreModule } from '../../../../../core/src/core/core.module'; import { HomeModule } from '../../../../../core/src/features/home/home.module'; import { SharedModule } from '../../../../../core/src/public-api'; import { ApplicationStateService } from '../../../shared/services/application-state.service'; +import { ApplicationDeploySourceTypes } from '../../applications/deploy-application/deploy-application-steps.types'; import { CardCfRecentAppsComponent } from '../card-cf-recent-apps/card-cf-recent-apps.component'; import { CompactAppCardComponent } from '../card-cf-recent-apps/compact-app-card/compact-app-card.component'; import { MDAppModule } from './../../../../../core/src/core/md.module'; @@ -30,6 +31,7 @@ import { CFHomeCardComponent } from './cfhome-card.component'; ], providers: [ ApplicationStateService, + ApplicationDeploySourceTypes, ] }) export class CFHomeCardModule { diff --git a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.html b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.html index eefa5a8c67..27a7354ad6 100644 --- a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.html +++ b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.scss b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.scss index 2a6328ad84..d467bec4f8 100644 --- a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.scss +++ b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.scss @@ -79,4 +79,14 @@ &.smaller { @include create(160px, 60px); } + + &.compact { + @include create(160px, 60px); + h3 { + font-size: 14px; + margin: 10px; + } + } + + } diff --git a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.ts b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.ts index 5b11d6ac48..f8b9bf391f 100644 --- a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.ts +++ b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.ts @@ -15,6 +15,8 @@ export class TileSelectorTileComponent { @Input() smaller = false; + @Input() compact = false; + @Output() tileSelect = new EventEmitter(); public onClick(tile: ITileConfig) { diff --git a/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.html b/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.html index 7f05aa5e7d..3fbe888568 100644 --- a/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.html +++ b/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.html @@ -12,6 +12,6 @@ + [active]="selected === tile || !selected" [smaller]="smallerTiles" [compact]="compactTiles"> \ No newline at end of file diff --git a/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.ts b/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.ts index bb2e6d1402..d81dfd93c9 100644 --- a/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.ts +++ b/src/frontend/packages/core/src/shared/components/tile-selector/tile-selector.component.ts @@ -13,6 +13,7 @@ export class TileSelectorComponent { public hiddenOptions: ITileConfig[] = []; public showingMore = false; @Input() smallerTiles = false; + @Input() compactTiles = false; @Input() dynamicSmallerTiles = 0; @Input() set options(options: ITileConfig[]) { if (!options) { From 1e7120483fc3fb0d5829beabe6d84a5be1582c6f Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Sat, 7 Nov 2020 22:01:18 +0000 Subject: [PATCH 28/74] Fix word wrap on favorite name --- .../home/favorites-meta-card/favorites-meta-card.component.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss index 73432811db..7146bac920 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss @@ -28,6 +28,7 @@ font-size: 14px; font-weight: bold; margin-bottom: 4px; + word-break: break-all; } &__type { font-size: 12px; From a3936470f6c1d54260419d04d3ac27d9afb72a85 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Sat, 7 Nov 2020 22:14:48 +0000 Subject: [PATCH 29/74] Add tool tip --- .../home-page-endpoint-card.component.html | 2 +- .../home-page-endpoint-card.component.scss | 1 + .../core/src/features/home/home/home-page.component.html | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index a992812f79..1cf1ac629c 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -14,7 +14,7 @@

-
+
warning
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss index f9a6ea4f47..39773ecfb8 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss @@ -47,6 +47,7 @@ opacity: 0.7; } &__error { + cursor: help; display: flex; } // Check if these are needed diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.html b/src/frontend/packages/core/src/features/home/home/home-page.component.html index 9914c06651..c3dada5478 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.html @@ -27,7 +27,7 @@

Home

- +
From a291548555063b88095e58d99779476c6787a935 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Sun, 8 Nov 2020 13:01:03 +0000 Subject: [PATCH 30/74] Style tidy ups --- .../dashboard-base.component.html | 2 +- .../home-page-endpoint-card.component.html | 26 ++++----- .../home-page-endpoint-card.component.scss | 58 +------------------ ...me-page-endpoint-card.component.theme.scss | 4 +- 4 files changed, 16 insertions(+), 74 deletions(-) diff --git a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html index d9b88d2fee..d2210b8c3f 100644 --- a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html +++ b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html @@ -33,7 +33,7 @@

-
+
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index 1cf1ac629c..7cfde62e55 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -1,27 +1,27 @@ - - -
- + + +
+
-
-

-
+
+

+
{{ endpoint.name }}

-
{{ definition.label }}
+
{{ definition.label }}
- +
-
+
warning
-
- +
+
- +
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss index 39773ecfb8..7a34726535 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.scss @@ -1,4 +1,4 @@ -.fav-meta-card { +.home-endpoint-card { border-radius: 0; display: flex; flex-direction: column; @@ -50,7 +50,6 @@ cursor: help; display: flex; } - // Check if these are needed &__type { $type-height: 20px; font-size: 13px; @@ -58,67 +57,12 @@ margin-top: -($type-height - 2px); opacity: .6; } - &__missing { - align-items: center; - display: flex; - flex-direction: column; - height: 100%; - justify-content: space-around; - padding: 10px; - padding-top: 20px; - &-text { - font-size: 20px; - font-weight: bold; - } - &-small-text { - margin-bottom: 20px; - } - } - &__clickable { - cursor: pointer; - } - &__icon { - margin-right: 4px; - opacity: .7; - text-align: center; - } - &__icon-panel { - display: flex; - justify-content: left; - width: 32px; - } &__panel { display: flex; :first-child { flex: 1; } } - &__disconnected { - align-self: center; - border-radius: 2px; - display: flex; - font-size: 14px; - font-weight: normal; - margin-right: 8px; - padding: 2px 4px; - } -} - -.error-details { - display: flex; - flex-direction: column; - padding: 0 20px; - &__value { - padding-bottom: 5px; - } - &__label { - font-size: 10px; - opacity: .6; - } -} - -.subtle-text { - opacity: .6; } .home-card { diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.theme.scss b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.theme.scss index a02aed0ccd..9b7a9ecb77 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.theme.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.theme.scss @@ -1,8 +1,7 @@ @mixin home-page-endpoint-card-theme($theme, $app-theme) { $foreground: map-get($theme, foreground); - .fav-meta-card { - //border: 1px solid mat-color($foreground, divider); + .home-endpoint-card { &__content { border-top: 1px solid mat-color($foreground, divider); } @@ -10,7 +9,6 @@ .home-card__right { border-left: 1px solid mat-color($foreground, divider); - mat-card { border: 1px solid mat-color($foreground, divider);; } From dcbd011905c430820f2f3dbe55de7261fabe722c Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Sun, 8 Nov 2020 13:07:07 +0000 Subject: [PATCH 31/74] Minor tidy ups --- .../favorites-side-panel.component.html | 4 +-- .../favorites-side-panel.component.ts | 2 -- .../src/shared/services/side-panel.service.ts | 30 +++++++------------ .../home/kubernetes-home-card.component.ts | 3 +- 4 files changed, 13 insertions(+), 26 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.html b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.html index 9cdf6ea603..9fffafab65 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.html +++ b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.html @@ -1,7 +1,5 @@ -
-
- +
diff --git a/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.ts b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.ts index f2f60d2ed6..cc886ef1dd 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.ts +++ b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.ts @@ -13,8 +13,6 @@ export class FavoritesSidePanelComponent implements PreviewableComponent { favorites$: Observable; name: string; - constructor() { } - setProps(props: { [key: string]: any; }): void { this.favorites$ = props.favorites$ this.name = props.endpoint.name; diff --git a/src/frontend/packages/core/src/shared/services/side-panel.service.ts b/src/frontend/packages/core/src/shared/services/side-panel.service.ts index 90217bd718..8ee7c87d02 100644 --- a/src/frontend/packages/core/src/shared/services/side-panel.service.ts +++ b/src/frontend/packages/core/src/shared/services/side-panel.service.ts @@ -11,9 +11,13 @@ import { Router } from '@angular/router'; import { asapScheduler, BehaviorSubject, Observable, Subject } from 'rxjs'; import { filter, observeOn, publishReplay, refCount, tap } from 'rxjs/operators'; +// Side Panel Modes export enum SidePanelMode { + // Modal = spans the full height of the window and overlaps the top bat Modal = 0, + // Normal = 600px width and height not overlapping title bar Normal = 1, + // Narrow = 400px width and height not overlapping title bar Narrow = 2, } @@ -61,43 +65,31 @@ export class SidePanelService { } /** - * Show the preview panel in a preview style - does not overlap title bar and colours are more muted + * Show the preview panel in the given mode - does not overlap title bar and colours are more muted */ - public show(component: object, props?: { [key: string]: any }, componentFactoryResolver?: ComponentFactoryResolver) { + public showMode( + mode: SidePanelMode, component: object, props?: { [key: string]: any }, componentFactoryResolver?: ComponentFactoryResolver) { if (!this.container) { throw new Error('SidePanelService: container must be set'); } this.render(component, props, componentFactoryResolver); - this.previewModeSubject.next(SidePanelMode.Normal); + this.previewModeSubject.next(mode); this.open(); } /** * Show the preview panel in a preview style - does not overlap title bar and colours are more muted */ - public showMode( - mode: SidePanelMode, component: object, props?: { [key: string]: any }, componentFactoryResolver?: ComponentFactoryResolver) { - if (!this.container) { - throw new Error('SidePanelService: container must be set'); - } - - this.render(component, props, componentFactoryResolver); - this.previewModeSubject.next(mode); - this.open(); + public show(component: object, props?: { [key: string]: any }, componentFactoryResolver?: ComponentFactoryResolver) { + this.showMode(SidePanelMode.Normal, component, props, componentFactoryResolver); } /** * Show the preview panel in a modal style - full height overlaps title bar */ public showModal(component: object, props?: { [key: string]: any }, componentFactoryResolver?: ComponentFactoryResolver) { - if (!this.container) { - throw new Error('SidePanelService: container must be set'); - } - - this.render(component, props, componentFactoryResolver); - this.previewModeSubject.next(SidePanelMode.Modal); - this.open(); + this.showMode(SidePanelMode.Modal, component, props, componentFactoryResolver); } private open() { diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts index 2ade3752e4..1d4926219c 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts @@ -35,8 +35,7 @@ export class KubernetesHomeCardComponent implements OnInit { public nodeCount$: Observable; public namespaceCount$: Observable; - constructor(private kubeEndpointService: KubernetesEndpointService) { - } + constructor(private kubeEndpointService: KubernetesEndpointService) { } ngOnInit() { const guid = this.endpoint.guid; From b55abe2f8e740e78892235d5d7c384684fddfdcd Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 9 Nov 2020 09:27:06 +0000 Subject: [PATCH 32/74] Check favorites exists before navigating to them --- .../cloud-foundry/src/cf-entity-generator.ts | 33 +++++++++------- .../favorites-meta-card.component.html | 3 +- .../favorites-meta-card.component.scss | 3 ++ .../favorites-meta-card.component.ts | 38 +++++++++++++++++++ .../endpoint-card/endpoint-card.component.ts | 5 +-- .../entity-catalog-entity.ts | 5 --- .../entity-catalog/entity-catalog.types.ts | 3 -- .../store/src/favorite-config-mapper.ts | 11 ------ 8 files changed, 64 insertions(+), 37 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index e91d2aa5b4..30079bf242 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -1,8 +1,8 @@ import { Compiler, Injector } from '@angular/core'; import { Action, Store } from '@ngrx/store'; import moment from 'moment'; -import { combineLatest, Observable, of } from 'rxjs'; -import { first, map } from 'rxjs/operators'; +import { combineLatest, Observable, of, OperatorFunction } from 'rxjs'; +import { filter, first, map, pairwise } from 'rxjs/operators'; import { BaseEndpointAuth } from '../../core/src/core/endpoint-auth'; import { urlValidationExpression } from '../../core/src/core/utils.service'; @@ -443,6 +443,15 @@ export function generateCFEntities(): StratosBaseCatalogEntity[] { ]; } +function entityFetchedWithoutError(): OperatorFunction { + return input$ => input$.pipe( + pairwise(), + filter(([oldV, newV]) => (oldV as any).fetching && !(newV as any).fetching), + map(([, newV]) => newV), + map(f => !(f as any).error) + ) +}; + function generateCFQuotaDefinitionEntity(endpointDefinition: StratosEndpointExtensionDefinition) { const definition: IStratosEntityDefinition = { type: quotaDefinitionEntityType, @@ -1208,11 +1217,9 @@ function generateCfApplicationEntity(endpointDefinition: StratosEndpointExtensio createdAt: moment(app.metadata.created_at).format('LLL'), name: app.entity.name, }), - getLink: metadata => `/applications/${metadata.cfGuid}/${metadata.guid}/summary`, + getLink: (metadata) => `/applications/${metadata.cfGuid}/${metadata.guid}/summary`, getGuid: metadata => metadata.guid, - getLines: () => ([ - ['Created', (meta) => meta.createdAt] - ]) + getIsValid: (metadata) => cfEntityCatalog.application.api.get(metadata.guid, metadata.cfGuid, {}).pipe(entityFetchedWithoutError()) }, actionBuilders: applicationActionBuilder }, @@ -1250,11 +1257,14 @@ function generateCfSpaceEntity(endpointDefinition: StratosEndpointExtensionDefin cfGuid: space.entity.cfGuid, createdAt: moment(space.metadata.created_at).format('LLL'), }), - getLines: () => ([ - ['Created', (meta) => meta.createdAt] - ]), getLink: metadata => `/cloud-foundry/${metadata.cfGuid}/organizations/${metadata.orgGuid}/spaces/${metadata.guid}/summary`, - getGuid: metadata => metadata.guid + getGuid: metadata => metadata.guid, + getIsValid: (metadata) => cfEntityCatalog.space.api.get(metadata.guid, metadata.cfGuid).pipe( + pairwise(), + filter(([oldV, newV]) => (oldV as any).fetching && !(newV as any).fetching), + map(([, newV]) => newV), + map(f => !(f as any).error) + ) } } ); @@ -1294,9 +1304,6 @@ function generateCfOrgEntity(endpointDefinition: StratosEndpointExtensionDefinit createdAt: moment(org.metadata.created_at).format('LLL'), }), getLink: metadata => `/cloud-foundry/${metadata.cfGuid}/organizations/${metadata.guid}`, - getLines: () => ([ - ['Created', (meta) => meta.createdAt] - ]), getGuid: metadata => metadata.guid } } diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html index cb1e36637a..1ea0c14512 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html @@ -1,4 +1,4 @@ - +
{{ icon.icon }}
@@ -6,5 +6,6 @@
{{ config.name }}
{{ prettyName }}
+ warning diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss index 7146bac920..98e82b4952 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.scss @@ -38,4 +38,7 @@ flex: 0; opacity: .7; } + &__error { + margin-right: 4px; + } } diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts index c41abcc8dc..1485863753 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts @@ -1,9 +1,15 @@ import { Component, Input } from '@angular/core'; +import { Router } from '@angular/router'; +import { of } from 'rxjs'; +import { first } from 'rxjs/operators'; import { IFavoritesMetaCardConfig } from '../../../../../../store/src/favorite-config-mapper'; import { entityCatalog } from '../../../../../../store/src/public-api'; import { IFavoriteEntity } from '../../../../../../store/src/types/user-favorite-manager.types'; import { IFavoriteMetadata, UserFavorite } from '../../../../../../store/src/types/user-favorites.types'; +import { UserFavoriteManager } from '../../../../../../store/src/user-favorite-manager'; +import { ConfirmationDialogConfig } from '../../../../shared/components/confirmation-dialog.config'; +import { ConfirmationDialogService } from '../../../../shared/components/confirmation-dialog.service'; interface FavoriteIconData { icon?: string; @@ -28,6 +34,14 @@ export class FavoritesMetaCardComponent { public icon: FavoriteIconData; + public valid = true; + + constructor( + private router: Router, + private confirmDialog: ConfirmationDialogService, + private userFavoriteManager: UserFavoriteManager, + ) {} + @Input() set favoriteEntity(favoriteEntity: IFavoriteEntity) { if (favoriteEntity) { @@ -44,4 +58,28 @@ export class FavoritesMetaCardComponent { this.config = config; } } + + openFavorite() { + if (!this.config.routerLink) { + return; + } + const entityDef = entityCatalog.getEntity(this.favorite.endpointType, this.favorite.entityType); + const isValidObs = (entityDef.builders.entityBuilder && entityDef.builders.entityBuilder.getIsValid) ? + entityDef.builders.entityBuilder.getIsValid(this.favorite.metadata) : of(true); + isValidObs.pipe(first()).subscribe(isValid => { + this.valid = isValid; + if (!isValid) { + const confirmation = new ConfirmationDialogConfig( + 'Remove this Favorite?', + `The ${this.favorite.entityType} for this favorite appears to have been deleted. Remove the favorite?`, + 'Remove', + true + ); + this.confirmDialog.open(confirmation, () => { this.userFavoriteManager.toggleFavorite(this.favorite); }) + } else { + // Navigate to the favorite + this.router.navigate([this.config.routerLink]); + } + }); + } } diff --git a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts index b06faaca61..4623bf868f 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts @@ -132,10 +132,7 @@ export class EndpointCardComponent extends CardCell implements On } ngOnInit() { - const favorite = this.favoritesConfigMapper.getFavoriteEndpointFromEntity(this.row); - if (favorite) { - this.favorite = this.favoritesConfigMapper.hasFavoriteConfigForType(favorite) ? favorite : null; - } + this.favorite = this.favoritesConfigMapper.getFavoriteEndpointFromEntity(this.row); const e = this.endpointCatalogEntity.definition; this.hasDetails = !!e && !!e.listDetailsComponent; } diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts index cbe9b83830..8d92c6ec86 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts @@ -328,11 +328,6 @@ export class StratosCatalogEndpointEntity extends StratosBaseCatalogEntity null, getGuid: metadata => metadata.guid, - getLines: () => [ - ['Address', (metadata) => metadata.address], - ['User', (metadata) => metadata.user], - ['Admin', (metadata) => metadata.admin] - ] }; // This is needed here for typing public definition: IStratosEndpointDefinition; diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts index 6ae0bdfff7..1ecdda2881 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts @@ -229,18 +229,15 @@ export type EntityRowBuilder = [string, (entity: T, store?: Store { getMetadata(entity: Y): T; - getStatusObservable?(entity: Y): Observable; // TODO This should be used in the entities schema. getGuid(entityMetadata: T): string; getLink?(entityMetadata: T): string; - getLines?(): EntityRowBuilder[]; getSubTypeLabels?(entityMetadata: T): { singular: string, plural: string, }; /** * Checks if the given entity is stil valid (e.g. has not been deleted) - * @param entityMetadata Entity metadata */ getIsValid?(entityMetadata: T): Observable; /** diff --git a/src/frontend/packages/store/src/favorite-config-mapper.ts b/src/frontend/packages/store/src/favorite-config-mapper.ts index f92a71dc95..ea77818b4f 100644 --- a/src/frontend/packages/store/src/favorite-config-mapper.ts +++ b/src/frontend/packages/store/src/favorite-config-mapper.ts @@ -23,7 +23,6 @@ export type TFavoritesMetaCardLine = [string, string | Observable]; export interface IFavoritesMetaCardConfig { type: string; - lines?: TFavoritesMetaCardLine[]; routerLink?: string; name: string; menuItems?: MenuItem[]; @@ -77,16 +76,13 @@ export class FavoritesConfigMapper { return (entity: T) => { if (!entity) { return { - lines: null, type: null, routerLink: null, name: null, menuItems: null }; } - const linesBuilders = catalogEntity.builders.entityBuilder.getLines ? catalogEntity.builders.entityBuilder.getLines() : []; return { - lines: linesBuilders.map(builder => ([builder[0], builder[1](entity)])) as [string, string | Observable][], type: catalogEntity.definition.type, routerLink: catalogEntity.builders.entityBuilder.getLink(entity), name: entity.name, @@ -95,13 +91,6 @@ export class FavoritesConfigMapper { }; } - /** - * Is there config for the given favorite type? - */ - public hasFavoriteConfigForType(favorite: IFavoriteTypeInfo) { - return !!this.getMapperFunction(favorite); - } - /** * For a given favorite, return the corresponding human readable type name */ From 5665e6a622a1eccaddc42cd25b1458c6b20b75f8 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 9 Nov 2020 09:29:22 +0000 Subject: [PATCH 33/74] Fix test imports --- .../cloud-foundry-space-summary.component.spec.ts | 10 ++++------ ...loud-foundry-organization-summary.component.spec.ts | 8 ++------ .../cloud-foundry-summary-tab.component.spec.ts | 8 ++------ 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component.spec.ts b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component.spec.ts index 9b1935631e..e973d6b546 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component.spec.ts +++ b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component.spec.ts @@ -6,12 +6,6 @@ import { generateCfBaseTestModules, } from '../../../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; import { CloudFoundrySpaceServiceMock } from '../../../../../../../../test-framework/cloud-foundry-space.service.mock'; -import { - CardCfRecentAppsComponent, -} from '../../../../../../../shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component'; -import { - CompactAppCardComponent, -} from '../../../../../../../shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component'; import { CardCfSpaceDetailsComponent, } from '../../../../../../../shared/components/cards/card-cf-space-details/card-cf-space-details.component'; @@ -22,6 +16,10 @@ import { import { CloudFoundryUserProvidedServicesService, } from '../../../../../../../shared/services/cloud-foundry-user-provided-services.service'; +import { CardCfRecentAppsComponent } from '../../../../../../home/card-cf-recent-apps/card-cf-recent-apps.component'; +import { + CompactAppCardComponent, +} from '../../../../../../home/card-cf-recent-apps/compact-app-card/compact-app-card.component'; import { CloudFoundryEndpointService } from '../../../../../services/cloud-foundry-endpoint.service'; import { CloudFoundryOrganizationService } from '../../../../../services/cloud-foundry-organization.service'; import { CloudFoundrySpaceService } from '../../../../../services/cloud-foundry-space.service'; diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-summary/cloud-foundry-organization-summary.component.spec.ts b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-summary/cloud-foundry-organization-summary.component.spec.ts index 639ae6ff62..ea18e34694 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-summary/cloud-foundry-organization-summary.component.spec.ts +++ b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-summary/cloud-foundry-organization-summary.component.spec.ts @@ -11,13 +11,9 @@ import { import { CardCfOrgUserDetailsComponent, } from '../../../../../shared/components/cards/card-cf-org-user-details/card-cf-org-user-details.component'; -import { - CardCfRecentAppsComponent, -} from '../../../../../shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component'; -import { - CompactAppCardComponent, -} from '../../../../../shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component'; import { CfUserPermissionDirective } from '../../../../../shared/directives/cf-user-permission/cf-user-permission.directive'; +import { CardCfRecentAppsComponent } from '../../../../home/card-cf-recent-apps/card-cf-recent-apps.component'; +import { CompactAppCardComponent } from '../../../../home/card-cf-recent-apps/compact-app-card/compact-app-card.component'; import { CloudFoundryOrganizationService } from '../../../services/cloud-foundry-organization.service'; import { CloudFoundryOrganizationSummaryComponent } from './cloud-foundry-organization-summary.component'; diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-summary-tab/cloud-foundry-summary-tab.component.spec.ts b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-summary-tab/cloud-foundry-summary-tab.component.spec.ts index 9c459b2db9..bb055e4564 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-summary-tab/cloud-foundry-summary-tab.component.spec.ts +++ b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-summary-tab/cloud-foundry-summary-tab.component.spec.ts @@ -6,12 +6,8 @@ import { generateTestCfEndpointServiceProvider, } from '../../../../../test-framework/cloud-foundry-endpoint-service.helper'; import { CardCfInfoComponent } from '../../../../shared/components/cards/card-cf-info/card-cf-info.component'; -import { - CardCfRecentAppsComponent, -} from '../../../../shared/components/cards/card-cf-recent-apps/card-cf-recent-apps.component'; -import { - CompactAppCardComponent, -} from '../../../../shared/components/cards/card-cf-recent-apps/compact-app-card/compact-app-card.component'; +import { CardCfRecentAppsComponent } from '../../../home/card-cf-recent-apps/card-cf-recent-apps.component'; +import { CompactAppCardComponent } from '../../../home/card-cf-recent-apps/compact-app-card/compact-app-card.component'; import { CloudFoundrySummaryTabComponent } from './cloud-foundry-summary-tab.component'; describe('CloudFoundrySummaryTabComponent', () => { From 787367057e776e8bb9004c8054c7a0f596113f18 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 9 Nov 2020 14:45:02 +0000 Subject: [PATCH 34/74] First round of tidy ups of the user favorites code --- .../cloud-foundry/src/cf-entity-generator.ts | 43 +++++----- src/frontend/packages/core/src/app.module.ts | 10 +-- .../entity-favorite-star.component.ts | 2 +- .../favorites-meta-card.component.html | 6 +- .../favorites-meta-card.component.ts | 38 ++++----- .../home-page-endpoint-card.component.html | 12 +-- .../home-page-endpoint-card.component.ts | 40 +++++----- .../table-cell-favorite.component.ts | 2 +- .../page-header/page-header.component.ts | 19 ++--- .../kubernetes/kubernetes-entity-generator.ts | 2 +- .../entity-catalog-entity.ts | 7 +- .../entity-catalog/entity-catalog.types.ts | 2 +- .../store/src/favorite-config-mapper.ts | 60 +------------- .../store/src/types/recently-visited.types.ts | 1 - .../src/types/user-favorite-manager.types.ts | 27 ------- .../store/src/types/user-favorites.types.ts | 80 ++++++++++++------- .../store/src/user-favorite-helpers.ts | 10 +-- .../store/src/user-favorite-manager.ts | 28 +++---- 18 files changed, 151 insertions(+), 238 deletions(-) delete mode 100644 src/frontend/packages/store/src/types/user-favorite-manager.types.ts diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index 30079bf242..9c7838b884 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -518,7 +518,7 @@ function generateCFAppEnvVarEntity(endpointDefinition: StratosEndpointExtensionD name: `Application environment variables (${ent.metadata.guid}).`, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: ent => ent.metadata.guid, }, }); return cfEntityCatalog.appEnvVar; @@ -543,7 +543,7 @@ function generateCFAppSummaryEntity(endpointDefinition: StratosEndpointExtension name: ent.name, guid: ent.guid }), - getGuid: metadata => metadata.guid, + getGuid: ent => ent.guid, } }); return cfEntityCatalog.appSummary; @@ -605,7 +605,7 @@ function generateCFInfoEntity(endpointDefinition: StratosEndpointExtensionDefini guid: info.entity.name, name: info.entity.name, }), - getGuid: metadata => metadata.guid, + getGuid: info => info.entity.name } } ); @@ -637,7 +637,7 @@ function generateCFUserProvidedServiceInstanceEntity(endpointDefinition: Stratos name: ent.entity.name, guid: ent.metadata.guid, }), - getGuid: metadata => metadata.guid, + getGuid: ent => ent.metadata.guid, }, } ); @@ -686,7 +686,7 @@ function generateCFAppStatsEntity(endpointDefinition: StratosEndpointExtensionDe name: ent.guid, guid: ent.guid }), - getGuid: metadata => metadata.name, + getGuid: ent => ent.guid, } }); return cfEntityCatalog.appStats; @@ -791,7 +791,7 @@ function generateCFServiceBindingEntity(endpointDefinition: StratosEndpointExten name: ent.metadata.guid, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: ent => ent.metadata.guid, } } ); @@ -822,7 +822,7 @@ function generateCFServiceEntity(endpointDefinition: StratosEndpointExtensionDef name: ent.entity.label, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: ent => ent.metadata.guid, }, } ); @@ -853,7 +853,7 @@ function generateCFServicePlanEntity(endpointDefinition: StratosEndpointExtensio name: ent.entity.name, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: ent => ent.metadata.guid, } } ); @@ -889,7 +889,7 @@ function generateCFServiceInstanceEntity(endpointDefinition: StratosEndpointExte name: ent.entity.name, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: ent => ent.metadata.guid, } } ); @@ -914,7 +914,7 @@ function generateCFUserEntity(endpointDefinition: StratosEndpointExtensionDefini name: ent.entity.username || ent.entity.guid || ent.metadata.guid, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: ent => ent.metadata.guid, } } ); @@ -945,7 +945,7 @@ function generateCFDomainEntity(endpointDefinition: StratosEndpointExtensionDefi name: ent.entity.name, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: ent => ent.metadata.guid, } } ); @@ -980,7 +980,7 @@ function generateGitCommitEntity(endpointDefinition: StratosEndpointExtensionDef name: ent.commit ? ent.commit.message || ent.sha : ent.sha, guid: ent.guid }), - getGuid: metadata => metadata.guid, + getGuid: ent => ent.guid, } } ); @@ -1056,7 +1056,7 @@ function generateEventEntity(endpointDefinition: StratosEndpointExtensionDefinit name: event.metadata.guid, }; }, - getGuid: metadata => metadata.guid, + getGuid: event => event.metadata.guid, } } ); @@ -1088,7 +1088,7 @@ function generateRouteEntity(endpointDefinition: StratosEndpointExtensionDefinit guid: app.metadata.guid, name: app.entity.domain_url, }), - getGuid: metadata => metadata.guid, + getGuid: app => app.metadata.guid, } } ); @@ -1119,7 +1119,7 @@ function generateStackEntity(endpointDefinition: StratosEndpointExtensionDefinit guid: app.metadata.guid, name: app.entity.name, }), - getGuid: metadata => metadata.guid, + getGuid: app => app.metadata.guid, } } ); @@ -1218,7 +1218,7 @@ function generateCfApplicationEntity(endpointDefinition: StratosEndpointExtensio name: app.entity.name, }), getLink: (metadata) => `/applications/${metadata.cfGuid}/${metadata.guid}/summary`, - getGuid: metadata => metadata.guid, + getGuid: app => app.metadata.guid, getIsValid: (metadata) => cfEntityCatalog.application.api.get(metadata.guid, metadata.cfGuid, {}).pipe(entityFetchedWithoutError()) }, actionBuilders: applicationActionBuilder @@ -1258,13 +1258,8 @@ function generateCfSpaceEntity(endpointDefinition: StratosEndpointExtensionDefin createdAt: moment(space.metadata.created_at).format('LLL'), }), getLink: metadata => `/cloud-foundry/${metadata.cfGuid}/organizations/${metadata.orgGuid}/spaces/${metadata.guid}/summary`, - getGuid: metadata => metadata.guid, - getIsValid: (metadata) => cfEntityCatalog.space.api.get(metadata.guid, metadata.cfGuid).pipe( - pairwise(), - filter(([oldV, newV]) => (oldV as any).fetching && !(newV as any).fetching), - map(([, newV]) => newV), - map(f => !(f as any).error) - ) + getGuid: entity => entity.metadata.guid, + getIsValid: (metadata) => cfEntityCatalog.space.api.get(metadata.guid, metadata.cfGuid).pipe(entityFetchedWithoutError()) } } ); @@ -1304,7 +1299,7 @@ function generateCfOrgEntity(endpointDefinition: StratosEndpointExtensionDefinit createdAt: moment(org.metadata.created_at).format('LLL'), }), getLink: metadata => `/cloud-foundry/${metadata.cfGuid}/organizations/${metadata.guid}`, - getGuid: metadata => metadata.guid + getGuid: entity => entity.metadata.guid, } } ); diff --git a/src/frontend/packages/core/src/app.module.ts b/src/frontend/packages/core/src/app.module.ts index 0ed0d1b159..dd46c00099 100644 --- a/src/frontend/packages/core/src/app.module.ts +++ b/src/frontend/packages/core/src/app.module.ts @@ -234,12 +234,11 @@ export class AppModule { ).subscribe( ([entities, recents]) => { Object.values(recents).forEach(recentEntity => { - const mapper = this.favoritesConfigMapper.getMapperFunction(recentEntity); const entityKey = entityCatalog.getEntityKey(recentEntity); if (entities[entityKey] && entities[entityKey][recentEntity.entityId]) { const entity = entities[entityKey][recentEntity.entityId]; const entityToMetadata = this.favoritesConfigMapper.getEntityMetadata(recentEntity, entity); - const name = mapper(entityToMetadata).name; + const name = entityToMetadata.name; if (name && name !== recentEntity.name) { // Update the entity name this.store.dispatch(new SetRecentlyVisitedEntityAction({ @@ -265,10 +264,9 @@ export class AppModule { if (entity) { const newMetadata = this.favoritesConfigMapper.getEntityMetadata(favorite, entity); if (this.metadataHasChanged(favorite.metadata, newMetadata)) { - stratosEntityCatalog.userFavorite.api.updateFavorite({ - ...favorite, - metadata: newMetadata - }); + const fav = this.userFavoriteManager.getUserFavoriteFromObject(favorite); + fav.metadata = newMetadata; + stratosEntityCatalog.userFavorite.api.updateFavorite(fav); } } } diff --git a/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.ts b/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.ts index c345e7d523..c522ecb4ea 100644 --- a/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.ts +++ b/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.ts @@ -18,7 +18,7 @@ export class EntityFavoriteStarComponent { @Input() set favorite(favorite: UserFavorite) { - const name = this.favoritesConfigMapper.getPrettyTypeName(favorite); + const name = favorite.getPrettyTypeName(); this.confirmationDialogConfig.message = `Are you sure you would you like to unfavorite this ${name ? name.toLocaleLowerCase() : 'favorite'}?`; this.isFavorite$ = this.userFavoriteManager.getIsFavoriteObservable(favorite); diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html index 1ea0c14512..9eecffee4d 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html @@ -1,10 +1,10 @@ - +
{{ icon.icon }}
-
{{ config.name }}
-
{{ prettyName }}
+
{{ name }}
+
{{ favoriteType }}
warning diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts index 1485863753..8d4929590f 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts @@ -5,17 +5,11 @@ import { first } from 'rxjs/operators'; import { IFavoritesMetaCardConfig } from '../../../../../../store/src/favorite-config-mapper'; import { entityCatalog } from '../../../../../../store/src/public-api'; -import { IFavoriteEntity } from '../../../../../../store/src/types/user-favorite-manager.types'; -import { IFavoriteMetadata, UserFavorite } from '../../../../../../store/src/types/user-favorites.types'; +import { FavoriteIconData, IFavoriteMetadata, UserFavorite } from '../../../../../../store/src/types/user-favorites.types'; import { UserFavoriteManager } from '../../../../../../store/src/user-favorite-manager'; import { ConfirmationDialogConfig } from '../../../../shared/components/confirmation-dialog.config'; import { ConfirmationDialogService } from '../../../../shared/components/confirmation-dialog.service'; -interface FavoriteIconData { - icon?: string; - iconFont?: string; -} - @Component({ selector: 'app-favorites-meta-card', templateUrl: './favorites-meta-card.component.html', @@ -28,10 +22,16 @@ export class FavoritesMetaCardComponent { public favorite: UserFavorite; - public prettyName: string; + // Type of favorite - e.g. 'Application' + public favoriteType: string; + + // Favorite name + public name: string; public config: IFavoritesMetaCardConfig; + public routerLink: string; + public icon: FavoriteIconData; public valid = true; @@ -43,24 +43,18 @@ export class FavoritesMetaCardComponent { ) {} @Input() - set favoriteEntity(favoriteEntity: IFavoriteEntity) { + set favoriteEntity(favoriteEntity: UserFavorite) { if (favoriteEntity) { - const { cardMapper, favorite, prettyName } = favoriteEntity; - this.favorite = favorite; - this.prettyName = prettyName || 'Unknown'; - const entityDef = entityCatalog.getEntity(this.favorite.endpointType, this.favorite.entityType); - this.icon = { - icon: entityDef.definition.icon, - iconFont: entityDef.definition.iconFont, - }; - - const config = cardMapper && favorite && favorite.metadata ? cardMapper(favorite.metadata) : null; - this.config = config; + this.favorite = favoriteEntity; + this.favoriteType = this.favorite.getPrettyTypeName(); + this.icon = this.favorite.getIcon(); + this.name = this.favorite.metadata.name + this.routerLink = this.favorite.getLink(); } } openFavorite() { - if (!this.config.routerLink) { + if (!this.routerLink) { return; } const entityDef = entityCatalog.getEntity(this.favorite.endpointType, this.favorite.entityType); @@ -78,7 +72,7 @@ export class FavoritesMetaCardComponent { this.confirmDialog.open(confirmation, () => { this.userFavoriteManager.toggleFavorite(this.favorite); }) } else { // Navigate to the favorite - this.router.navigate([this.config.routerLink]); + this.router.navigate([this.routerLink]); } }); } diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html index 7cfde62e55..10984223ac 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.html @@ -11,11 +11,13 @@

{{ definition.label }}
-
- -
-
- warning +
+
+ +
+
+ warning +
diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index 627190ba82..d2b7a2630d 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -34,6 +34,13 @@ const CUTOFF_SHOW_SHORTCUTS_ON_LEFT = 10; const MAX_SHORTCUTS = 5; const MAX_LINKS = 5; +// Loading/error status of the card +enum Status { + OK = 0, + Loading = 1, + Error = 2, +} + @Component({ selector: 'app-home-page-endpoint-card', templateUrl: './home-page-endpoint-card.component.html', @@ -76,10 +83,9 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi public link: string; - load$: Observable; - loadSubj = new BehaviorSubject(false); - isLoading = false; - isError = false; + // Status = 0 OK, 1 Loading, 2 Error + status$: Observable; + status = new BehaviorSubject(Status.OK); private ref: ComponentRef; private sub: Subscription; @@ -100,7 +106,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi private compiler: Compiler, private injector: Injector, ) { - this.load$ = this.loadSubj.asObservable(); + this.status$ = this.status.asObservable(); } ngAfterViewInit() { @@ -115,11 +121,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi ngOnInit() { // Favorites for this endpoint - this.favorites$ = this.userFavoriteManager.getFavoritesForEndpoint(this.endpoint.guid).pipe( - map(f => { - return f.map(item => this.userFavoriteManager.mapToHydrated(item)); - }) - ); + this.favorites$ = this.userFavoriteManager.getFavoritesForEndpoint(this.endpoint.guid); this.entity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type) if (this.entity) { @@ -132,14 +134,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi } this.fullView = this.definition.homeCard && this.definition.homeCard.fullView; - - const mapper = this.favoritesConfigMapper.getMapperFunction(this.favorite); - if (mapper && this.favorite.metadata) { - const p = mapper(this.favorite.metadata); - if (p) { - this.link = p.routerLink; - } - } + this.link = this.favorite.getLink(); } this.links$ = combineLatest([this.favorites$, this.layout$.asObservable()]).pipe( @@ -220,17 +215,18 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi // Ask the card to load itself loadCard() { if (this.canLoad && this.ref && this.ref.instance && this.ref.instance.load) { - this.isLoading = true; + this.status.next(Status.Loading); const loadObs = this.ref.instance.load() || of(true); // Timeout after 10 seconds this.sub = loadObs.pipe(timeout(10000), filter(v => v === true), first()).subscribe(() => { this.loaded.next(); - this.isLoading = false; + setTimeout(() => this.status.next(Status.OK), 0); + this.sub.unsubscribe(); }, () => { this.loaded.next(); - this.isLoading = false; - this.isError = true; + this.status.next(Status.Error); + this.sub.unsubscribe(); }); } } diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component.ts b/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component.ts index cb239478f9..cb6f6a8e61 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component.ts @@ -42,7 +42,7 @@ export class TableCellFavoriteComponent extends private createUserFavorite() { if (this.row && this.config) { this.favorite = this.config.createUserFavorite(this.row); - this.canFavorite = !!this.favoritesConfigMapper.getMapperFunction(this.favorite); + this.canFavorite = this.favorite.canFavorite(); } } } diff --git a/src/frontend/packages/core/src/shared/components/page-header/page-header.component.ts b/src/frontend/packages/core/src/shared/components/page-header/page-header.component.ts index 010c2b9b90..fb7d9b7188 100644 --- a/src/frontend/packages/core/src/shared/components/page-header/page-header.component.ts +++ b/src/frontend/packages/core/src/shared/components/page-header/page-header.component.ts @@ -9,7 +9,6 @@ import { map, startWith } from 'rxjs/operators'; import { ToggleSideNav } from '../../../../../store/src/actions/dashboard-actions'; import { AddRecentlyVisitedEntityAction } from '../../../../../store/src/actions/recently-visited.actions'; import { AppState } from '../../../../../store/src/app-state'; -import { EntityCatalogHelpers } from '../../../../../store/src/entity-catalog/entity-catalog.helper'; import { FavoritesConfigMapper } from '../../../../../store/src/favorite-config-mapper'; import { selectIsMobile } from '../../../../../store/src/selectors/dashboard.selectors'; import { InternalEventSeverity } from '../../../../../store/src/types/internal-events.types'; @@ -89,26 +88,18 @@ export class PageHeaderComponent implements OnDestroy, AfterViewInit { @Input() set favorite(favorite: UserFavorite) { if (favorite && (!this.pFavorite || (favorite.guid !== this.pFavorite.guid))) { - this.pFavorite = favorite; - const mapperFunction = this.favoritesConfigMapper.getMapperFunction(favorite); - const prettyType = this.favoritesConfigMapper.getPrettyTypeName(favorite); - const prettyEndpointType = this.favoritesConfigMapper.getPrettyTypeName({ - endpointType: favorite.endpointType, - entityType: EntityCatalogHelpers.endpointType - }); - if (mapperFunction) { - const { name, routerLink } = mapperFunction(favorite.metadata); + if (favorite.canFavorite()) { + this.pFavorite = favorite; this.store.dispatch(new AddRecentlyVisitedEntityAction({ guid: favorite.guid, date: moment().valueOf(), entityType: favorite.entityType, endpointType: favorite.endpointType, entityId: favorite.entityId, - name, - routerLink, - prettyType, + name: favorite.metadata.name, + routerLink: favorite.getLink(), + prettyType: favorite.getPrettyTypeName(), endpointId: favorite.endpointId, - prettyEndpointType: prettyEndpointType === prettyType ? null : prettyEndpointType })); } } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index 71c729afba..68f78b15a5 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -337,7 +337,7 @@ function generateNamespacesEntity(endpointDefinition: StratosEndpointExtensionDe }; }, getLink: metadata => `/kubernetes/${metadata.kubeGuid}/namespaces/${metadata.name}y`, - getGuid: metadata => metadata.guid, + getGuid: namespace => namespace.metadata.uid, } }); return kubeEntityCatalog.namespace; diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts index 8d92c6ec86..ae263dd3dc 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts @@ -179,11 +179,10 @@ export class StratosBaseCatalogEntity< } public getGuidFromEntity(entity: Y) { - if (!this.builders.entityBuilder || !this.builders.entityBuilder.getGuid || !this.builders.entityBuilder.getMetadata) { - return null; + if (this.builders.entityBuilder && this.builders.entityBuilder.getGuid) { + return this.builders.entityBuilder.getGuid(entity); } - const metadata = this.builders.entityBuilder.getMetadata(entity); - return this.builders.entityBuilder.getGuid(metadata); + return null; } public getEndpointGuidFromEntity(entity: Y & EntityPipelineEntity) { diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts index 1ecdda2881..963c3a0852 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts @@ -230,7 +230,7 @@ export type EntityRowBuilder = [string, (entity: T, store?: Store { getMetadata(entity: Y): T; // TODO This should be used in the entities schema. - getGuid(entityMetadata: T): string; + getGuid(entity: Y): string; getLink?(entityMetadata: T): string; getSubTypeLabels?(entityMetadata: T): { singular: string, diff --git a/src/frontend/packages/store/src/favorite-config-mapper.ts b/src/frontend/packages/store/src/favorite-config-mapper.ts index ea77818b4f..b34d01bf14 100644 --- a/src/frontend/packages/store/src/favorite-config-mapper.ts +++ b/src/frontend/packages/store/src/favorite-config-mapper.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; import { entityCatalog } from './entity-catalog/entity-catalog'; import { StratosBaseCatalogEntity } from './entity-catalog/entity-catalog-entity/entity-catalog-entity'; @@ -10,17 +9,6 @@ import { MenuItem } from './types/menu-item.types'; import { EntityRequestAction } from './types/request.types'; import { IFavoriteMetadata, IFavoriteTypeInfo, UserFavorite, UserFavoriteEndpoint } from './types/user-favorites.types'; - -export interface IFavoriteTypes { - type: string; - prettyName: string; -} - -/** - * [label, value] - */ -export type TFavoritesMetaCardLine = [string, string | Observable]; - export interface IFavoritesMetaCardConfig { type: string; routerLink?: string; @@ -61,7 +49,6 @@ export interface IFavoriteActionGenerators { }) export class FavoritesConfigMapper { private mapperKeySeparator = '-'; - // private mappers: IFavoriteMappers = {}; constructor() { } public getMapperKeyFromFavoriteInfo(favoriteInfo: IFavoriteTypeInfo) { const { endpointType, entityType } = favoriteInfo; @@ -69,55 +56,14 @@ export class FavoritesConfigMapper { } /** - * For a given favorite, return the corresponding favorite meta card mapper - */ - public getMapperFunction(favorite: IFavoriteTypeInfo) { - const catalogEntity = entityCatalog.getEntity(favorite.endpointType, favorite.entityType); - return (entity: T) => { - if (!entity) { - return { - type: null, - routerLink: null, - name: null, - menuItems: null - }; - } - return { - type: catalogEntity.definition.type, - routerLink: catalogEntity.builders.entityBuilder.getLink(entity), - name: entity.name, - menuItems: catalogEntity.builders.entityBuilder.getActions ? catalogEntity.builders.entityBuilder.getActions(entity) : null - }; - }; - } - - /** - * For a given favorite, return the corresponding human readable type name - */ - public getPrettyTypeName(favorite: IFavoriteTypeInfo) { - const catalogEntity = entityCatalog.getEntity(favorite.endpointType, favorite.entityType); - return catalogEntity ? catalogEntity.definition.label : null; - } - - /** - * For a given favorite, return the corresponding hydration action + * For a given favorite, return the corresponding metadata */ public getEntityMetadata(favorite: IFavoriteTypeInfo, entity: any) { const catalogEntity = entityCatalog.getEntity(favorite.endpointType, favorite.entityType); return catalogEntity ? catalogEntity.builders.entityBuilder.getMetadata(entity) : null; } - /** - * For a given endpoint type, return the list of possible favorite types - */ - public getAllTypesForEndpoint(endpointType: string): IFavoriteTypes[] { - return entityCatalog.getAllEntitiesForEndpointType(endpointType).map(catalogEntity => ({ - type: catalogEntity.definition.type, - prettyName: catalogEntity.definition.label - })); - } - - private buildFavoriteFromCatalogEntity( + private buildFavoriteFromCatalogEntity( catalogEntity: StratosBaseCatalogEntity, entity: any, endpointId: string @@ -127,7 +73,7 @@ export class FavoritesConfigMapper { const endpointType = isEndpoint ? catalogEntity.getTypeAndSubtype().type : entityDefinition.endpoint.type; const entityType = isEndpoint ? EntityCatalogHelpers.endpointType : entityDefinition.type; const metadata = catalogEntity.builders.entityBuilder.getMetadata(entity); - const guid = isEndpoint ? null : catalogEntity.builders.entityBuilder.getGuid(metadata); + const guid = isEndpoint ? null : catalogEntity.builders.entityBuilder.getGuid(entity); if (!endpointId) { console.error('User favourite - buildFavoriteFromCatalogEntity - endpointId is undefined'); } diff --git a/src/frontend/packages/store/src/types/recently-visited.types.ts b/src/frontend/packages/store/src/types/recently-visited.types.ts index 95e581663c..ab5327ba0d 100644 --- a/src/frontend/packages/store/src/types/recently-visited.types.ts +++ b/src/frontend/packages/store/src/types/recently-visited.types.ts @@ -8,7 +8,6 @@ export interface IRecentlyVisitedEntity extends IFavoriteTypeInfo { date: number; entityId: string; prettyType: string; - prettyEndpointType: string; endpointId: string; routerLink?: string; } diff --git a/src/frontend/packages/store/src/types/user-favorite-manager.types.ts b/src/frontend/packages/store/src/types/user-favorite-manager.types.ts deleted file mode 100644 index 3b170661a2..0000000000 --- a/src/frontend/packages/store/src/types/user-favorite-manager.types.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { TFavoriteMapperFunction } from '../favorite-config-mapper'; -import { IEndpointFavMetadata, IFavoriteMetadata, UserFavorite } from './user-favorites.types'; - -export interface IFavoriteEntity { - type: string; - prettyName: string; - cardMapper: TFavoriteMapperFunction; - favorite: UserFavorite; -} - -export interface IGroupedFavorites { - endpoint: IHydrationResults; - entities: IHydrationResults[]; -} - -export interface IAllFavorites { - fetching: boolean; - error: boolean; - entityGroups: IGroupedFavorites[]; -} - -export interface IHydrationResults { - type: string; - cardMapper: TFavoriteMapperFunction; - prettyName: string; - favorite: UserFavorite; -} diff --git a/src/frontend/packages/store/src/types/user-favorites.types.ts b/src/frontend/packages/store/src/types/user-favorites.types.ts index b92b043b41..ddcc43cfc9 100644 --- a/src/frontend/packages/store/src/types/user-favorites.types.ts +++ b/src/frontend/packages/store/src/types/user-favorites.types.ts @@ -1,4 +1,6 @@ -import { IEntityMetadata } from '../entity-catalog/entity-catalog.types'; +import { entityCatalog } from '../entity-catalog/entity-catalog'; +import { IEntityMetadata, IStratosEntityBuilder, IStratosEntityDefinition } from '../entity-catalog/entity-catalog.types'; +import { StratosBaseCatalogEntity } from './../entity-catalog/entity-catalog-entity/entity-catalog-entity'; export const userFavoritesPaginationKey = 'userFavorites'; @@ -38,11 +40,19 @@ export interface BackendUserFavorite { metadata: string; } +export interface FavoriteIconData { + icon?: string; + iconFont?: string; +} + const favoriteGuidSeparator = '-'; export class UserFavorite implements IFavoriteTypeInfo { public guid: string; + private catalogEntity: StratosBaseCatalogEntity; + private entityBuilder: IStratosEntityBuilder; + constructor( public endpointId: string, public endpointType: string, @@ -53,32 +63,14 @@ export class UserFavorite implement public entityId?: string, public metadata: T = null ) { - this.guid = UserFavorite.buildFavoriteStoreEntityGuid(this); - } - - static buildFavoriteStoreEntityGuid(favorite: UserFavorite) { - const { - entityId, - endpointId, - entityType, - endpointType, - } = favorite; - return [ - entityId, - endpointId, - entityType, - endpointType, - ] - .reduce((newArray, value) => { - if (value) { - return [ - ...newArray, - value, - ]; - } - return newArray; - }, []) - .join(favoriteGuidSeparator); + // Set the guid for this favorite + this.buildFavoriteStoreEntityGuid(); + this.catalogEntity = entityCatalog.getEntity(this.endpointType, this.entityType); + if (this.catalogEntity && this.catalogEntity.builders && this.catalogEntity.builders.entityBuilder) { + this.entityBuilder = this.catalogEntity.builders.entityBuilder; + } else { + this.entityBuilder = {} as IStratosEntityBuilder; + } } static getEntityGuidFromFavoriteGuid(favoriteGuid: string): string { @@ -95,6 +87,40 @@ export class UserFavorite implement ''); } } + + public canFavorite(): boolean { + // What do we need to be able to favorite an entity? + // TODO + return true; + } + + // Get the link to navigate to the view for the given entity backing this user favorite + public getLink(): string { + return this.entityBuilder.getLink ? this.entityBuilder.getLink(this.metadata) : null; + } + + // Get the type name, e.g. 'Application' + public getPrettyTypeName(): string { + return this.catalogEntity && this.catalogEntity.definition ? this.catalogEntity.definition.label : 'Unknown'; + } + + // Get icon data for the favorite + public getIcon(): FavoriteIconData { + const defn = this.catalogEntity && this.catalogEntity.definition ? this.catalogEntity.definition : {} as IStratosEntityDefinition; + return { + icon: defn.icon || 'help', + iconFont: defn.iconFont + } + } + + private buildFavoriteStoreEntityGuid() { + this.guid = [this.entityId, this.endpointId, this.entityType, this.endpointType].reduce((newArray, value) => { + if (value) { + return [ ...newArray, value ]; + } + return newArray; + }, []).join(favoriteGuidSeparator); + } } export type UserFavoriteEndpoint = UserFavorite; diff --git a/src/frontend/packages/store/src/user-favorite-helpers.ts b/src/frontend/packages/store/src/user-favorite-helpers.ts index 84f81be582..d9601d075d 100644 --- a/src/frontend/packages/store/src/user-favorite-helpers.ts +++ b/src/frontend/packages/store/src/user-favorite-helpers.ts @@ -25,13 +25,9 @@ export function getFavoriteFromEntity) { if (favorite.entityType !== 'endpoint') { - const endpointFav = { - ...favorite - }; - endpointFav.entityId = null; - endpointFav.entityType = 'endpoint'; - endpointFav.guid = UserFavorite.buildFavoriteStoreEntityGuid(endpointFav); - return endpointFav; + return new UserFavorite( + favorite.endpointId, favorite.endpointType, 'endpoint', null, favorite.metadata + ); } return favorite; } diff --git a/src/frontend/packages/store/src/user-favorite-manager.ts b/src/frontend/packages/store/src/user-favorite-manager.ts index 030ff5dc5b..1ca9c5e4b9 100644 --- a/src/frontend/packages/store/src/user-favorite-manager.ts +++ b/src/frontend/packages/store/src/user-favorite-manager.ts @@ -4,7 +4,6 @@ import { combineLatest, Observable, of } from 'rxjs'; import { filter, map, switchMap, tap } from 'rxjs/operators'; import { GeneralEntityAppState, IRequestEntityTypeState } from './app-state'; -import { entityCatalog } from './entity-catalog/entity-catalog'; import { FavoritesConfigMapper } from './favorite-config-mapper'; import { endpointEntitiesSelector } from './selectors/endpoint.selectors'; import { @@ -16,9 +15,14 @@ import { import { isFavorite } from './selectors/favorite.selectors'; import { stratosEntityCatalog } from './stratos-entity-catalog'; import { IUserFavoritesGroups } from './types/favorite-groups.types'; -import { IGroupedFavorites, IHydrationResults } from './types/user-favorite-manager.types'; import { IEndpointFavMetadata, IFavoriteMetadata, UserFavorite } from './types/user-favorites.types'; + +interface IGroupedFavorites { + endpoint: UserFavorite; + entities: UserFavorite[]; +} + @Injectable({ providedIn: 'root' }) @@ -86,7 +90,7 @@ export class UserFavoriteManager { favoriteEntities: IRequestEntityTypeState> ): Observable { const endpointFav = favoriteEntities[endpointFavoriteGuid] as UserFavorite; - const entities = favEntitiesGuid.map(guid => this.mapToHydrated(favoriteEntities[guid])); + const entities = favEntitiesGuid.map(guid => this.getUserFavoriteFromObject(favoriteEntities[guid])); if (!endpointFav) { return this.store.select(endpointEntitiesSelector).pipe( map(endpoints => { @@ -95,26 +99,19 @@ export class UserFavoriteManager { return this.favoritesConfigMapper.getFavoriteEndpointFromEntity(endpointEntity); }), map(endpointFavorite => ({ - endpoint: this.mapToHydrated(endpointFavorite), + endpoint: this.getUserFavoriteFromObject(endpointFavorite), entities })) ); } return of({ - endpoint: this.mapToHydrated(endpointFav), + endpoint: this.getUserFavoriteFromObject(endpointFav), entities }); } - public mapToHydrated = (favorite: UserFavorite): IHydrationResults => { - const catalogEntity = entityCatalog.getEntity(favorite.endpointType, favorite.entityType); - - return { - type: catalogEntity.definition.type, - cardMapper: this.favoritesConfigMapper.getMapperFunction(favorite), - prettyName: catalogEntity.definition.label, - favorite - }; + public getUserFavoriteFromObject = (f: UserFavorite): UserFavorite => { + return new UserFavorite(f.endpointId, f.endpointType, f.entityType, f.entityId, f.metadata); } public hydrateFavorite(favorite: UserFavorite): IFavoriteMetadata { @@ -140,7 +137,8 @@ export class UserFavoriteManager { const result = []; Object.values(favs).forEach(f => { if (f.endpointId === endpointID && f.entityId) { - result.push(f); + // Ensure we actually have a UserFavorite object and not a struct + result.push(this.getUserFavoriteFromObject(f)); } }) return result; From ff7b50d614403dfe4e82245669324034c4f572a7 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 9 Nov 2020 15:08:04 +0000 Subject: [PATCH 35/74] Further refactor to reduce the complexity of user favorites --- .../cloud-foundry/src/cf-favorites-helpers.ts | 6 +- .../application-tabs-base.component.ts | 6 +- .../cloud-foundry-tabs-base.component.ts | 6 +- ...oud-foundry-organization-base.component.ts | 6 +- .../cloud-foundry-space-base.component.ts | 6 +- .../list-types/app/card/card-app.component.ts | 8 +- .../cf-org-card/cf-org-card.component.ts | 6 +- .../cf-space-card/cf-space-card.component.ts | 6 +- src/frontend/packages/core/src/app.module.ts | 6 +- .../entity-favorite-star.component.spec.ts | 7 -- .../entity-favorite-star.component.ts | 2 - .../favorites-meta-card.component.ts | 3 - .../home-page-endpoint-card.component.ts | 4 +- .../meta-card.component.spec.ts | 5 - .../meta-card-base/meta-card.component.ts | 6 +- .../table-cell-favorite.component.ts | 3 +- .../endpoint-card/endpoint-card.component.ts | 6 +- .../endpoint/endpoints-list-config.service.ts | 6 +- .../page-header/page-header.component.ts | 2 - .../kubernetes/kubernetes-entity-generator.ts | 8 +- .../kubernetes-namespace.component.ts | 6 +- .../kubernetes-tab-base.component.ts | 6 +- .../entity-catalog/entity-catalog.types.ts | 4 +- .../store/src/favorite-config-mapper.ts | 110 ------------------ .../store/src/user-favorite-helpers.ts | 6 +- .../store/src/user-favorite-manager.ts | 73 ++++++++++-- 26 files changed, 117 insertions(+), 196 deletions(-) delete mode 100644 src/frontend/packages/store/src/favorite-config-mapper.ts diff --git a/src/frontend/packages/cloud-foundry/src/cf-favorites-helpers.ts b/src/frontend/packages/cloud-foundry/src/cf-favorites-helpers.ts index 8f19d39a21..1f5716442d 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-favorites-helpers.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-favorites-helpers.ts @@ -1,15 +1,15 @@ import { IEntityMetadata } from '../../store/src/entity-catalog/entity-catalog.types'; -import { FavoritesConfigMapper } from '../../store/src/favorite-config-mapper'; import { UserFavorite } from '../../store/src/types/user-favorites.types'; +import { UserFavoriteManager } from './../../store/src/user-favorite-manager'; import { CfAPIResource } from './store/types/cf-api.types'; export function getFavoriteFromCfEntity( entity, entityType: string, - favoritesConfigMapper: FavoritesConfigMapper + userFavoriteManager: UserFavoriteManager ): UserFavorite { if (isCfEntity(entity as CfAPIResource)) { - return favoritesConfigMapper.getFavoriteFromEntity( + return userFavoriteManager.getFavoriteFromEntity( entityType, 'cf', entity.entity.cfGuid, diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/application/application-tabs-base/application-tabs-base.component.ts b/src/frontend/packages/cloud-foundry/src/features/applications/application/application-tabs-base/application-tabs-base.component.ts index 12db72f9ba..d995b7e4c8 100644 --- a/src/frontend/packages/cloud-foundry/src/features/applications/application/application-tabs-base/application-tabs-base.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/applications/application/application-tabs-base/application-tabs-base.component.ts @@ -20,13 +20,13 @@ import { IPageSideNavTab } from '../../../../../../core/src/features/dashboard/p import { IHeaderBreadcrumb } from '../../../../../../core/src/shared/components/page-header/page-header.types'; import { RouterNav } from '../../../../../../store/src/actions/router.actions'; import { entityCatalog } from '../../../../../../store/src/entity-catalog/entity-catalog'; -import { FavoritesConfigMapper } from '../../../../../../store/src/favorite-config-mapper'; import { EntitySchema } from '../../../../../../store/src/helpers/entity-schema'; import { ActionState } from '../../../../../../store/src/reducers/api-request-reducer/types'; import { endpointEntitiesSelector } from '../../../../../../store/src/selectors/endpoint.selectors'; import { APIResource } from '../../../../../../store/src/types/api.types'; import { EndpointModel } from '../../../../../../store/src/types/endpoint.types'; import { getFavoriteFromEntity } from '../../../../../../store/src/user-favorite-helpers'; +import { UserFavoriteManager } from '../../../../../../store/src/user-favorite-manager'; import { UpdateExistingApplication } from '../../../../actions/application.actions'; import { IApp, IOrganization, ISpace } from '../../../../cf-api.types'; import { CF_ENDPOINT_TYPE } from '../../../../cf-types'; @@ -48,7 +48,7 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { public favorite$ = this.applicationService.app$.pipe( filter(app => !!app), - map(app => getFavoriteFromEntity(app.entity, applicationEntityType, this.favoritesConfigMapper, CF_ENDPOINT_TYPE)) + map(app => getFavoriteFromEntity(app.entity, applicationEntityType, this.userFavoriteManager, CF_ENDPOINT_TYPE)) ); isBusyUpdating$: Observable<{ updating: boolean }>; @@ -62,7 +62,7 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { private ngZone: NgZone, private currentUserPermissionsService: CurrentUserPermissionsService, scmService: GitSCMService, - private favoritesConfigMapper: FavoritesConfigMapper, + private userFavoriteManager: UserFavoriteManager, private appPollingService: ApplicationPollingService ) { const catalogEntity = entityCatalog.getEntity(CF_ENDPOINT_TYPE, applicationEntityType); diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts b/src/frontend/packages/cloud-foundry/src/features/cf/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts index 2818e5005e..e4b2877b4a 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/cf/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts @@ -13,8 +13,8 @@ import { import { CurrentUserPermissionsService } from '../../../../../core/src/core/permissions/current-user-permissions.service'; import { environment } from '../../../../../core/src/environments/environment.prod'; import { IPageSideNavTab } from '../../../../../core/src/features/dashboard/page-side-nav/page-side-nav.component'; -import { FavoritesConfigMapper } from '../../../../../store/src/favorite-config-mapper'; import { UserFavoriteEndpoint } from '../../../../../store/src/types/user-favorites.types'; +import { UserFavoriteManager } from '../../../../../store/src/user-favorite-manager'; import { CfCurrentUserPermissions } from '../../../user-permissions/cf-user-permissions-checkers'; import { CloudFoundryEndpointService } from '../services/cloud-foundry-endpoint.service'; @@ -45,12 +45,12 @@ export class CloudFoundryTabsBaseComponent implements OnInit { public cfEndpointService: CloudFoundryEndpointService, private currentUserPermissionsService: CurrentUserPermissionsService, endpointsService: EndpointsService, - favoritesConfigMapper: FavoritesConfigMapper + userFavoriteManager: UserFavoriteManager ) { this.favorite$ = endpointsService.endpoints$.pipe( first(), map(endpoints => endpoints[this.cfEndpointService.cfGuid]), - map(endpoint => favoritesConfigMapper.getFavoriteEndpointFromEntity(endpoint)) + map(endpoint => userFavoriteManager.getFavoriteEndpointFromEntity(endpoint)) ); const firehoseHidden$ = this.currentUserPermissionsService diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-base/cloud-foundry-organization-base.component.ts b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-base/cloud-foundry-organization-base.component.ts index 682da43532..22788d8d9d 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-base/cloud-foundry-organization-base.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-base/cloud-foundry-organization-base.component.ts @@ -12,10 +12,10 @@ import { import { environment } from '../../../../../../../core/src/environments/environment.prod'; import { IPageSideNavTab } from '../../../../../../../core/src/features/dashboard/page-side-nav/page-side-nav.component'; import { IHeaderBreadcrumb } from '../../../../../../../core/src/shared/components/page-header/page-header.types'; -import { FavoritesConfigMapper } from '../../../../../../../store/src/favorite-config-mapper'; import { EntitySchema } from '../../../../../../../store/src/helpers/entity-schema'; import { UserFavorite } from '../../../../../../../store/src/types/user-favorites.types'; import { getFavoriteFromEntity } from '../../../../../../../store/src/user-favorite-helpers'; +import { UserFavoriteManager } from '../../../../../../../store/src/user-favorite-manager'; import { cfEntityFactory } from '../../../../../cf-entity-factory'; import { organizationEntityType } from '../../../../../cf-entity-types'; import { IOrgFavMetadata } from '../../../../../cf-metadata-types'; @@ -94,12 +94,12 @@ export class CloudFoundryOrganizationBaseComponent { constructor( public cfEndpointService: CloudFoundryEndpointService, public cfOrgService: CloudFoundryOrganizationService, - favoritesConfigMapper: FavoritesConfigMapper + userFavoriteManager: UserFavoriteManager ) { this.schema = cfEntityFactory(organizationEntityType); this.favorite$ = cfOrgService.org$.pipe( first(), - map(org => getFavoriteFromEntity(org.entity, organizationEntityType, favoritesConfigMapper, CF_ENDPOINT_TYPE)) + map(org => getFavoriteFromEntity(org.entity, organizationEntityType, userFavoriteManager, CF_ENDPOINT_TYPE)) ); this.name$ = cfOrgService.org$.pipe( map(org => org.entity.entity.name), diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-spaces/cloud-foundry-space-base/cloud-foundry-space-base.component.ts b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-spaces/cloud-foundry-space-base/cloud-foundry-space-base.component.ts index c1cea96630..4ee427cd1a 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-spaces/cloud-foundry-space-base/cloud-foundry-space-base.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-spaces/cloud-foundry-space-base/cloud-foundry-space-base.component.ts @@ -15,9 +15,9 @@ import { IPageSideNavTab } from '../../../../../../../../core/src/features/dashb import { ConfirmationDialogService } from '../../../../../../../../core/src/shared/components/confirmation-dialog.service'; import { IHeaderBreadcrumb } from '../../../../../../../../core/src/shared/components/page-header/page-header.types'; import { RouterNav } from '../../../../../../../../store/src/actions/router.actions'; -import { FavoritesConfigMapper } from '../../../../../../../../store/src/favorite-config-mapper'; import { UserFavorite } from '../../../../../../../../store/src/types/user-favorites.types'; import { getFavoriteFromEntity } from '../../../../../../../../store/src/user-favorite-helpers'; +import { UserFavoriteManager } from '../../../../../../../../store/src/user-favorite-manager'; import { CFAppState } from '../../../../../../cf-app-state'; import { cfEntityFactory } from '../../../../../../cf-entity-factory'; import { spaceEntityType } from '../../../../../../cf-entity-types'; @@ -112,10 +112,10 @@ export class CloudFoundrySpaceBaseComponent implements OnDestroy { public cfOrgService: CloudFoundryOrganizationService, private store: Store, private confirmDialog: ConfirmationDialogService, - favoritesConfigMapper: FavoritesConfigMapper + userFavoriteManager: UserFavoriteManager ) { this.favorite$ = cfSpaceService.space$.pipe( - map(space => getFavoriteFromEntity(space.entity, spaceEntityType, favoritesConfigMapper, CF_ENDPOINT_TYPE)) + map(space => getFavoriteFromEntity(space.entity, spaceEntityType, userFavoriteManager, CF_ENDPOINT_TYPE)) ); this.isFetching$ = cfSpaceService.space$.pipe( map(space => space.entityRequestInfo.fetching) diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app/card/card-app.component.ts b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app/card/card-app.component.ts index 39a18e5336..e9ead4fc10 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app/card/card-app.component.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app/card/card-app.component.ts @@ -7,11 +7,11 @@ import { CFAppState } from '../../../../../../../../cloud-foundry/src/cf-app-sta import { applicationEntityType } from '../../../../../../../../cloud-foundry/src/cf-entity-types'; import { IAppFavMetadata } from '../../../../../../../../cloud-foundry/src/cf-metadata-types'; import { CardCell } from '../../../../../../../../core/src/shared/components/list/list.types'; -import { FavoritesConfigMapper } from '../../../../../../../../store/src/favorite-config-mapper'; import { APIResource } from '../../../../../../../../store/src/types/api.types'; import { ComponentEntityMonitorConfig, StratosStatus } from '../../../../../../../../store/src/types/shared.types'; import { UserFavorite } from '../../../../../../../../store/src/types/user-favorites.types'; import { getFavoriteFromEntity } from '../../../../../../../../store/src/user-favorite-helpers'; +import { UserFavoriteManager } from '../../../../../../../../store/src/user-favorite-manager'; import { IApp, ISpace } from '../../../../../../cf-api.types'; import { cfEntityFactory } from '../../../../../../cf-entity-factory'; import { CF_ENDPOINT_TYPE } from '../../../../../../cf-types'; @@ -38,9 +38,7 @@ export class CardAppComponent extends CardCell> implements OnI constructor( private store: Store, private appStateService: ApplicationStateService, - private favoritesConfigMapper: FavoritesConfigMapper, - - + private userFavoriteManager: UserFavoriteManager ) { super(); } @@ -54,7 +52,7 @@ export class CardAppComponent extends CardCell> implements OnI this.row.entity.space_guid ); - this.favorite = getFavoriteFromEntity(this.row, applicationEntityType, this.favoritesConfigMapper, CF_ENDPOINT_TYPE); + this.favorite = getFavoriteFromEntity(this.row, applicationEntityType, this.userFavoriteManager, CF_ENDPOINT_TYPE); const initState = this.appStateService.get(this.row.entity, null); this.applicationState$ = ApplicationService.getApplicationState( diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-orgs/cf-org-card/cf-org-card.component.ts b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-orgs/cf-org-card/cf-org-card.component.ts index fee067f62f..c68460bf3c 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-orgs/cf-org-card/cf-org-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-orgs/cf-org-card/cf-org-card.component.ts @@ -13,7 +13,6 @@ import { ConfirmationDialogConfig } from '../../../../../../../../core/src/share import { ConfirmationDialogService } from '../../../../../../../../core/src/shared/components/confirmation-dialog.service'; import { CardCell } from '../../../../../../../../core/src/shared/components/list/list.types'; import { RouterNav } from '../../../../../../../../store/src/actions/router.actions'; -import { FavoritesConfigMapper } from '../../../../../../../../store/src/favorite-config-mapper'; import { EntityMonitorFactory } from '../../../../../../../../store/src/monitors/entity-monitor.factory.service'; import { PaginationMonitorFactory } from '../../../../../../../../store/src/monitors/pagination-monitor.factory'; import { APIResource } from '../../../../../../../../store/src/types/api.types'; @@ -22,6 +21,7 @@ import { MenuItem } from '../../../../../../../../store/src/types/menu-item.type import { ComponentEntityMonitorConfig, StratosStatus } from '../../../../../../../../store/src/types/shared.types'; import { IFavoriteMetadata, UserFavorite } from '../../../../../../../../store/src/types/user-favorites.types'; import { getFavoriteFromEntity } from '../../../../../../../../store/src/user-favorite-helpers'; +import { UserFavoriteManager } from '../../../../../../../../store/src/user-favorite-manager'; import { IApp, IOrganization } from '../../../../../../cf-api.types'; import { cfEntityFactory } from '../../../../../../cf-entity-factory'; import { getStartedAppInstanceCount } from '../../../../../../cf.helpers'; @@ -64,7 +64,7 @@ export class CfOrgCardComponent extends CardCell> imp private confirmDialog: ConfirmationDialogService, private paginationMonitorFactory: PaginationMonitorFactory, private emf: EntityMonitorFactory, - private favoritesConfigMapper: FavoritesConfigMapper + private userFavoriteManager: UserFavoriteManager ) { super(); @@ -96,7 +96,7 @@ export class CfOrgCardComponent extends CardCell> imp refCount() ); - this.favorite = getFavoriteFromEntity(this.row, organizationEntityType, this.favoritesConfigMapper, CF_ENDPOINT_TYPE); + this.favorite = getFavoriteFromEntity(this.row, organizationEntityType, this.userFavoriteManager, CF_ENDPOINT_TYPE); const allApps$: Observable[]> = this.cfEndpointService.appsPagObs.hasEntities$.pipe( switchMap(hasAll => hasAll ? this.cfEndpointService.getAppsInOrgViaAllApps(this.row) : observableOf(null)) diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-spaces/cf-space-card/cf-space-card.component.ts b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-spaces/cf-space-card/cf-space-card.component.ts index 784692eab6..a42131a229 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-spaces/cf-space-card/cf-space-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/cf-spaces/cf-space-card/cf-space-card.component.ts @@ -14,7 +14,6 @@ import { ConfirmationDialogConfig } from '../../../../../../../../core/src/share import { ConfirmationDialogService } from '../../../../../../../../core/src/shared/components/confirmation-dialog.service'; import { CardCell } from '../../../../../../../../core/src/shared/components/list/list.types'; import { RouterNav } from '../../../../../../../../store/src/actions/router.actions'; -import { FavoritesConfigMapper } from '../../../../../../../../store/src/favorite-config-mapper'; import { EntityMonitorFactory } from '../../../../../../../../store/src/monitors/entity-monitor.factory.service'; import { PaginationMonitorFactory } from '../../../../../../../../store/src/monitors/pagination-monitor.factory'; import { APIResource } from '../../../../../../../../store/src/types/api.types'; @@ -23,6 +22,7 @@ import { MenuItem } from '../../../../../../../../store/src/types/menu-item.type import { ComponentEntityMonitorConfig, StratosStatus } from '../../../../../../../../store/src/types/shared.types'; import { UserFavorite } from '../../../../../../../../store/src/types/user-favorites.types'; import { getFavoriteFromEntity } from '../../../../../../../../store/src/user-favorite-helpers'; +import { UserFavoriteManager } from '../../../../../../../../store/src/user-favorite-manager'; import { IApp, ISpace } from '../../../../../../cf-api.types'; import { cfEntityFactory } from '../../../../../../cf-entity-factory'; import { CF_ENDPOINT_TYPE } from '../../../../../../cf-types'; @@ -68,7 +68,7 @@ export class CfSpaceCardComponent extends CardCell> implemen private confirmDialog: ConfirmationDialogService, private paginationMonitorFactory: PaginationMonitorFactory, private emf: EntityMonitorFactory, - private favoritesConfigMapper: FavoritesConfigMapper + private userFavoriteManager: UserFavoriteManager ) { super(); } @@ -77,7 +77,7 @@ export class CfSpaceCardComponent extends CardCell> implemen this.spaceGuid = this.row.metadata.guid; this.entityConfig = new ComponentEntityMonitorConfig(this.spaceGuid, cfEntityFactory(spaceEntityType)); this.orgGuid = this.cfOrgService.orgGuid; - this.favorite = getFavoriteFromEntity(this.row, spaceEntityType, this.favoritesConfigMapper, CF_ENDPOINT_TYPE); + this.favorite = getFavoriteFromEntity(this.row, spaceEntityType, this.userFavoriteManager, CF_ENDPOINT_TYPE); this.cardMenu = [ { label: 'Edit', diff --git a/src/frontend/packages/core/src/app.module.ts b/src/frontend/packages/core/src/app.module.ts index dd46c00099..5615ada6ad 100644 --- a/src/frontend/packages/core/src/app.module.ts +++ b/src/frontend/packages/core/src/app.module.ts @@ -13,7 +13,6 @@ import { EntityCatalogModule } from '../../store/src/entity-catalog.module'; import { entityCatalog } from '../../store/src/entity-catalog/entity-catalog'; import { EntityCatalogHelper } from '../../store/src/entity-catalog/entity-catalog-entity/entity-catalog.service'; import { EntityCatalogHelpers } from '../../store/src/entity-catalog/entity-catalog.helper'; -import { FavoritesConfigMapper } from '../../store/src/favorite-config-mapper'; import { endpointEntityType, STRATOS_ENDPOINT_TYPE } from '../../store/src/helpers/stratos-entity-factory'; import { getAPIRequestDataState, selectEntity } from '../../store/src/selectors/api.selectors'; import { internalEventStateSelector } from '../../store/src/selectors/internal-events.selectors'; @@ -133,7 +132,6 @@ export class AppModule { private store: Store, eventService: GlobalEventService, private userFavoriteManager: UserFavoriteManager, - private favoritesConfigMapper: FavoritesConfigMapper, ech: EntityCatalogHelper ) { EntityCatalogHelpers.SetEntityCatalogHelper(ech); @@ -237,7 +235,7 @@ export class AppModule { const entityKey = entityCatalog.getEntityKey(recentEntity); if (entities[entityKey] && entities[entityKey][recentEntity.entityId]) { const entity = entities[entityKey][recentEntity.entityId]; - const entityToMetadata = this.favoritesConfigMapper.getEntityMetadata(recentEntity, entity); + const entityToMetadata = this.userFavoriteManager.getEntityMetadata(recentEntity, entity); const name = entityToMetadata.name; if (name && name !== recentEntity.name) { // Update the entity name @@ -262,7 +260,7 @@ export class AppModule { }) : entityCatalog.getEntityKey(favorite); const entity = entities[entityKey][favorite.entityId || favorite.endpointId]; if (entity) { - const newMetadata = this.favoritesConfigMapper.getEntityMetadata(favorite, entity); + const newMetadata = this.userFavoriteManager.getEntityMetadata(favorite, entity); if (this.metadataHasChanged(favorite.metadata, newMetadata)) { const fav = this.userFavoriteManager.getUserFavoriteFromObject(favorite); fav.metadata = newMetadata; diff --git a/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.spec.ts b/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.spec.ts index 067a4e1f69..91d3dcf22f 100644 --- a/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.spec.ts +++ b/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.spec.ts @@ -2,7 +2,6 @@ import { OverlayContainer } from '@angular/cdk/overlay'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BehaviorSubject, of } from 'rxjs'; -import { FavoritesConfigMapper } from '../../../../store/src/favorite-config-mapper'; import { PaginationMonitorFactory } from '../../../../store/src/monitors/pagination-monitor.factory'; import { IFavoriteMetadata, UserFavorite } from '../../../../store/src/types/user-favorites.types'; import { UserFavoriteManager } from '../../../../store/src/user-favorite-manager'; @@ -16,7 +15,6 @@ describe('EntityFavoriteStarComponent', () => { let fixture: ComponentFixture; let element: HTMLElement; let userFavoriteManager: UserFavoriteManager; - let favoritesConfigMapper: FavoritesConfigMapper; let favorite: UserFavorite; let overlayContainerElement: HTMLElement; @@ -43,7 +41,6 @@ describe('EntityFavoriteStarComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(EntityFavoriteStarComponent); userFavoriteManager = TestBed.get(UserFavoriteManager); - favoritesConfigMapper = TestBed.get(FavoritesConfigMapper); component = fixture.componentInstance; fixture.detectChanges(); element = fixture.nativeElement; @@ -70,7 +67,6 @@ describe('EntityFavoriteStarComponent', () => { it('should set isFavorite based on favorite', () => { spyOn(userFavoriteManager, 'getIsFavoriteObservable').and.returnValue(of(true)); - spyOn(favoritesConfigMapper, 'getPrettyTypeName').and.returnValue('prettyName'); component.favorite = favorite; fixture.detectChanges(); @@ -79,7 +75,6 @@ describe('EntityFavoriteStarComponent', () => { it('should set isFavorite based on favorite [2]', () => { spyOn(userFavoriteManager, 'getIsFavoriteObservable').and.returnValue(of(false)); - spyOn(favoritesConfigMapper, 'getPrettyTypeName').and.returnValue('prettyName'); component.favorite = favorite; fixture.detectChanges(); @@ -88,7 +83,6 @@ describe('EntityFavoriteStarComponent', () => { it('should toggle favorite if clicked', () => { const isFavorite$ = new BehaviorSubject(false); - spyOn(favoritesConfigMapper, 'getPrettyTypeName').and.returnValue('prettyName'); spyOn(userFavoriteManager, 'toggleFavorite').and.callFake(() => isFavorite$.next(!isFavorite$.getValue())); spyOn(userFavoriteManager, 'getIsFavoriteObservable').and.returnValue(isFavorite$); component.favorite = favorite; @@ -103,7 +97,6 @@ describe('EntityFavoriteStarComponent', () => { }); xit('should toggle even through confirmation dialog if confirm removal', () => { - spyOn(favoritesConfigMapper, 'getPrettyTypeName').and.returnValue('prettyName'); spyOn(userFavoriteManager, 'toggleFavorite'); spyOn(userFavoriteManager, 'getIsFavoriteObservable').and.returnValue(of(true)); component.confirmRemoval = true; diff --git a/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.ts b/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.ts index c522ecb4ea..68bf8793ac 100644 --- a/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.ts +++ b/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.ts @@ -2,7 +2,6 @@ import { Component, Input } from '@angular/core'; import { Observable } from 'rxjs'; import { first, tap } from 'rxjs/operators'; -import { FavoritesConfigMapper } from '../../../../store/src/favorite-config-mapper'; import { IFavoriteMetadata, UserFavorite } from '../../../../store/src/types/user-favorites.types'; import { UserFavoriteManager } from '../../../../store/src/user-favorite-manager'; import { ConfirmationDialogConfig } from '../../shared/components/confirmation-dialog.config'; @@ -38,7 +37,6 @@ export class EntityFavoriteStarComponent { private confirmDialog: ConfirmationDialogService, public endpointsService: EndpointsService, private userFavoriteManager: UserFavoriteManager, - private favoritesConfigMapper: FavoritesConfigMapper ) { } diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts index 8d4929590f..e7925a8fdb 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts @@ -3,7 +3,6 @@ import { Router } from '@angular/router'; import { of } from 'rxjs'; import { first } from 'rxjs/operators'; -import { IFavoritesMetaCardConfig } from '../../../../../../store/src/favorite-config-mapper'; import { entityCatalog } from '../../../../../../store/src/public-api'; import { FavoriteIconData, IFavoriteMetadata, UserFavorite } from '../../../../../../store/src/types/user-favorites.types'; import { UserFavoriteManager } from '../../../../../../store/src/user-favorite-manager'; @@ -28,8 +27,6 @@ export class FavoritesMetaCardComponent { // Favorite name public name: string; - public config: IFavoritesMetaCardConfig; - public routerLink: string; public icon: FavoriteIconData; diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index d2b7a2630d..1944e2d3b3 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -20,7 +20,6 @@ import { HomeCardShortcut, IStratosEndpointDefinition, } from '../../../../../../store/src/entity-catalog/entity-catalog.types'; -import { FavoritesConfigMapper } from '../../../../../../store/src/favorite-config-mapper'; import { EndpointModel, entityCatalog } from '../../../../../../store/src/public-api'; import { UserFavoriteManager } from '../../../../../../store/src/user-favorite-manager'; import { SidePanelMode, SidePanelService } from '../../../../shared/services/side-panel.service'; @@ -100,7 +99,6 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi fullView = false; constructor( - private favoritesConfigMapper: FavoritesConfigMapper, private userFavoriteManager: UserFavoriteManager, private sidePanelService: SidePanelService, private compiler: Compiler, @@ -126,7 +124,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi this.entity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type) if (this.entity) { this.definition = this.entity.definition; - this.favorite = this.favoritesConfigMapper.getFavoriteEndpointFromEntity(this.endpoint); + this.favorite = this.userFavoriteManager.getFavoriteEndpointFromEntity(this.endpoint); // Get the list of shortcuts for the endpoint for the given endpoint ID if (this.definition.homeCard && this.definition.homeCard.shortcuts) { diff --git a/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.spec.ts b/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.spec.ts index 2ccb12a2be..4e96c84f7f 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.spec.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.spec.ts @@ -5,7 +5,6 @@ import { StoreModule } from '@ngrx/store'; import { createBasicStoreModule } from '@stratosui/store/testing'; import { Observable, of } from 'rxjs'; -import { FavoritesConfigMapper } from '../../../../../../../../store/src/favorite-config-mapper'; import { EntitySchema } from '../../../../../../../../store/src/helpers/entity-schema'; import { EntityMonitorFactory } from '../../../../../../../../store/src/monitors/entity-monitor.factory.service'; import { ComponentEntityMonitorConfig, StratosStatus } from '../../../../../../../../store/src/types/shared.types'; @@ -70,7 +69,6 @@ describe('MetaCardComponent', () => { let fixture: ComponentFixture; let element: HTMLElement; let entityMonitorFactory: EntityMonitorFactoryMock; - let favoritesConfigMapper: FavoritesConfigMapper; beforeEach(async(() => { TestBed.configureTestingModule({ @@ -94,7 +92,6 @@ describe('MetaCardComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(WrapperComponent); entityMonitorFactory = TestBed.get(EntityMonitorFactory); - favoritesConfigMapper = TestBed.get(FavoritesConfigMapper); component = fixture.componentInstance.metaCard; fixture.detectChanges(); element = fixture.debugElement.nativeElement; @@ -143,7 +140,6 @@ describe('MetaCardComponent', () => { }); it('should show star if favoritable', () => { - spyOn(favoritesConfigMapper, 'getPrettyTypeName').and.returnValue('prettyName'); component.favorite = favorite; fixture.detectChanges(); @@ -151,7 +147,6 @@ describe('MetaCardComponent', () => { }); it('should set favorite from entityConfig if not set', () => { - spyOn(favoritesConfigMapper, 'getPrettyTypeName').and.returnValue('prettyName'); spyOn(favoriteHelpers, 'getFavoriteFromEntity').and.returnValue(favorite); component.entityConfig = entityConfig; fixture.detectChanges(); diff --git a/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.ts b/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.ts index 35e79c78c2..9d1b01583b 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.ts @@ -2,12 +2,12 @@ import { Component, ContentChild, ContentChildren, Input, OnDestroy, QueryList } import { combineLatest, Observable, of as observableOf, of, Subscription } from 'rxjs'; import { first, map, tap } from 'rxjs/operators'; -import { FavoritesConfigMapper } from '../../../../../../../../store/src/favorite-config-mapper'; import { EntityMonitorFactory } from '../../../../../../../../store/src/monitors/entity-monitor.factory.service'; import { MenuItem } from '../../../../../../../../store/src/types/menu-item.types'; import { ComponentEntityMonitorConfig, StratosStatus } from '../../../../../../../../store/src/types/shared.types'; import { IFavoriteMetadata, UserFavorite } from '../../../../../../../../store/src/types/user-favorites.types'; import { getFavoriteFromEntity } from '../../../../../../../../store/src/user-favorite-helpers'; +import { UserFavoriteManager } from '../../../../../../../../store/src/user-favorite-manager'; import { safeUnsubscribe } from '../../../../../../core/utils.service'; import { MetaCardItemComponent } from '../meta-card-item/meta-card-item.component'; import { MetaCardTitleComponent } from '../meta-card-title/meta-card-title.component'; @@ -75,7 +75,7 @@ export class MetaCardComponent implements OnDestroy { tap(entity => this.favorite = getFavoriteFromEntity( entity, entityConfig.schema.entityType, - this.favoritesConfigMapper, + this.userFavoriteManager, entityConfig.schema.endpointType )) ).subscribe(); @@ -117,7 +117,7 @@ export class MetaCardComponent implements OnDestroy { constructor( private entityMonitorFactory: EntityMonitorFactory, - private favoritesConfigMapper: FavoritesConfigMapper, + private userFavoriteManager: UserFavoriteManager, ) { } ngOnDestroy() { diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component.ts b/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component.ts index cb6f6a8e61..ba31d0adbd 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component.ts @@ -1,6 +1,5 @@ import { Component, Input } from '@angular/core'; -import { FavoritesConfigMapper } from '../../../../../../../store/src/favorite-config-mapper'; import { IFavoriteMetadata, UserFavorite } from '../../../../../../../store/src/types/user-favorites.types'; import { TableCellCustom } from '../../list.types'; import { ITableColumn } from '../table.types'; @@ -16,7 +15,7 @@ export interface TableCellFavoriteComponentConfig extends TableCellCustom { - constructor(private favoritesConfigMapper: FavoritesConfigMapper) { + constructor() { super(); } diff --git a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts index 4623bf868f..14185bd5a9 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts @@ -18,11 +18,11 @@ import { entityCatalog } from '../../../../../../../../store/src/entity-catalog/ import { StratosCatalogEndpointEntity, } from '../../../../../../../../store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity'; -import { FavoritesConfigMapper } from '../../../../../../../../store/src/favorite-config-mapper'; import { EndpointModel } from '../../../../../../../../store/src/types/endpoint.types'; import { MenuItem } from '../../../../../../../../store/src/types/menu-item.types'; import { StratosStatus } from '../../../../../../../../store/src/types/shared.types'; import { UserFavoriteEndpoint } from '../../../../../../../../store/src/types/user-favorites.types'; +import { UserFavoriteManager } from '../../../../../../../../store/src/user-favorite-manager'; import { safeUnsubscribe } from '../../../../../../core/utils.service'; import { coreEndpointListDetailsComponents } from '../../../../../../features/endpoints/endpoint-helpers'; import { createMetaCardMenuItemSeparator } from '../../../list-cards/meta-card/meta-card-base/meta-card.component'; @@ -125,14 +125,14 @@ export class EndpointCardComponent extends CardCell implements On private store: Store, private endpointListHelper: EndpointListHelper, private componentFactoryResolver: ComponentFactoryResolver, - private favoritesConfigMapper: FavoritesConfigMapper, + private userFavoriteManager: UserFavoriteManager, ) { super(); this.endpointIds$ = this.endpointIds.asObservable(); } ngOnInit() { - this.favorite = this.favoritesConfigMapper.getFavoriteEndpointFromEntity(this.row); + this.favorite = this.userFavoriteManager.getFavoriteEndpointFromEntity(this.row); const e = this.endpointCatalogEntity.definition; this.hasDetails = !!e && !!e.listDetailsComponent; } diff --git a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts index 217c3b43b9..adc1ac43e4 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts @@ -7,13 +7,13 @@ import { ListView } from '../../../../../../../store/src/actions/list.actions'; import { SetClientFilter } from '../../../../../../../store/src/actions/pagination.actions'; import { AppState } from '../../../../../../../store/src/app-state'; import { entityCatalog } from '../../../../../../../store/src/entity-catalog/entity-catalog'; -import { FavoritesConfigMapper } from '../../../../../../../store/src/favorite-config-mapper'; import { EntityMonitorFactory } from '../../../../../../../store/src/monitors/entity-monitor.factory.service'; import { InternalEventMonitorFactory } from '../../../../../../../store/src/monitors/internal-event-monitor.factory'; import { PaginationMonitorFactory } from '../../../../../../../store/src/monitors/pagination-monitor.factory'; import { stratosEntityCatalog } from '../../../../../../../store/src/stratos-entity-catalog'; import { EndpointModel } from '../../../../../../../store/src/types/endpoint.types'; import { PaginationEntityState } from '../../../../../../../store/src/types/pagination.types'; +import { UserFavoriteManager } from '../../../../../../../store/src/user-favorite-manager'; import { createTableColumnFavorite } from '../../list-table/table-cell-favorite/table-cell-favorite.component'; import { ITableColumn } from '../../list-table/table.types'; import { @@ -114,11 +114,11 @@ export class EndpointsListConfigService implements IListConfig { entityMonitorFactory: EntityMonitorFactory, internalEventMonitorFactory: InternalEventMonitorFactory, endpointListHelper: EndpointListHelper, - favoritesConfigMapper: FavoritesConfigMapper, + userFavoriteManager: UserFavoriteManager ) { this.singleActions = endpointListHelper.endpointActions(); const favoriteCell = createTableColumnFavorite( - (row: EndpointModel) => favoritesConfigMapper.getFavoriteEndpointFromEntity(row) + (row: EndpointModel) => userFavoriteManager.getFavoriteEndpointFromEntity(row) ); this.columns.push(favoriteCell); diff --git a/src/frontend/packages/core/src/shared/components/page-header/page-header.component.ts b/src/frontend/packages/core/src/shared/components/page-header/page-header.component.ts index fb7d9b7188..6f5be3c971 100644 --- a/src/frontend/packages/core/src/shared/components/page-header/page-header.component.ts +++ b/src/frontend/packages/core/src/shared/components/page-header/page-header.component.ts @@ -9,7 +9,6 @@ import { map, startWith } from 'rxjs/operators'; import { ToggleSideNav } from '../../../../../store/src/actions/dashboard-actions'; import { AddRecentlyVisitedEntityAction } from '../../../../../store/src/actions/recently-visited.actions'; import { AppState } from '../../../../../store/src/app-state'; -import { FavoritesConfigMapper } from '../../../../../store/src/favorite-config-mapper'; import { selectIsMobile } from '../../../../../store/src/selectors/dashboard.selectors'; import { InternalEventSeverity } from '../../../../../store/src/types/internal-events.types'; import { StratosStatus } from '../../../../../store/src/types/shared.types'; @@ -149,7 +148,6 @@ export class PageHeaderComponent implements OnDestroy, AfterViewInit { private tabNavService: TabNavService, private router: Router, eventService: GlobalEventService, - private favoritesConfigMapper: FavoritesConfigMapper, private userProfileService: UserProfileService, private cups: CurrentUserPermissionsService, private endpointsService: EndpointsService, diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index 68f78b15a5..03e0fb1acc 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -14,9 +14,9 @@ import { StratosEndpointExtensionDefinition, } from '../../../store/src/entity-catalog/entity-catalog.types'; import { EndpointAuthTypeConfig, EndpointType } from '../../../store/src/extension-types'; -import { FavoritesConfigMapper } from '../../../store/src/favorite-config-mapper'; import { metricEntityType } from '../../../store/src/helpers/stratos-entity-factory'; import { IFavoriteMetadata, UserFavorite } from '../../../store/src/types/user-favorites.types'; +import { UserFavoriteManager } from '../../../store/src/user-favorite-manager'; import { KubernetesAWSAuthFormComponent } from './auth-forms/kubernetes-aws-auth-form/kubernetes-aws-auth-form.component'; import { KubernetesCertsAuthFormComponent, @@ -393,11 +393,9 @@ function generateMetricEntity(endpointDefinition: StratosEndpointExtensionDefini function getFavoriteFromKubeEntity( entity, entityType: string, - favoritesConfigMapper: FavoritesConfigMapper + userFavoriteManager: UserFavoriteManager ): UserFavorite { - console.log('Hello'); - console.log(entity); - return favoritesConfigMapper.getFavoriteFromEntity( + return userFavoriteManager.getFavoriteFromEntity( entityType, KUBERNETES_ENDPOINT_TYPE, entity.kubeGuid, diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts index 5bd6b02238..2d2ae26d3b 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts @@ -5,8 +5,8 @@ import { filter, map } from 'rxjs/operators'; import { IAppFavMetadata } from '../../../../cloud-foundry/src/cf-metadata-types'; import { IHeaderBreadcrumb } from '../../../../core/src/shared/components/page-header/page-header.types'; -import { FavoritesConfigMapper } from '../../../../store/src/favorite-config-mapper'; import { getFavoriteFromEntity } from '../../../../store/src/user-favorite-helpers'; +import { UserFavoriteManager } from '../../../../store/src/user-favorite-manager'; import { kubernetesNamespacesEntityType } from '../kubernetes-entity-factory'; import { BaseKubeGuid } from '../kubernetes-page.types'; import { KubernetesEndpointService } from '../services/kubernetes-endpoint.service'; @@ -47,7 +47,7 @@ export class KubernetesNamespaceComponent { public kubeEndpointService: KubernetesEndpointService, public kubeNamespaceService: KubernetesNamespaceService, public analysisService: KubernetesAnalysisService, - private favoritesConfigMapper: FavoritesConfigMapper, + private userFavoriteManager: UserFavoriteManager, ) { this.breadcrumbs$ = kubeEndpointService.endpoint$.pipe( map(endpoint => ([{ @@ -75,7 +75,7 @@ export class KubernetesNamespaceComponent { prettyText: 'Kubernetes Namespace', }, kubernetesNamespacesEntityType, - this.favoritesConfigMapper, + this.userFavoriteManager, KUBERNETES_ENDPOINT_TYPE )) ); diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-tab-base/kubernetes-tab-base.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-tab-base/kubernetes-tab-base.component.ts index c250a2de07..4b3b5e61ea 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-tab-base/kubernetes-tab-base.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-tab-base/kubernetes-tab-base.component.ts @@ -3,8 +3,8 @@ import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { first, map, startWith } from 'rxjs/operators'; -import { FavoritesConfigMapper } from '../../../../store/src/favorite-config-mapper'; import { UserFavoriteEndpoint } from '../../../../store/src/types/user-favorites.types'; +import { UserFavoriteManager } from '../../../../store/src/user-favorite-manager'; import { BaseKubeGuid } from '../kubernetes-page.types'; import { KubernetesEndpointService } from '../services/kubernetes-endpoint.service'; import { KubernetesAnalysisService } from '../services/kubernetes.analysis.service'; @@ -41,7 +41,7 @@ export class KubernetesTabBaseComponent implements OnInit { constructor( public kubeEndpointService: KubernetesEndpointService, - public favoritesConfigMapper: FavoritesConfigMapper, + public userFavoriteManager: UserFavoriteManager, public analysisService: KubernetesAnalysisService, ) { this.tabLinks = [ @@ -62,7 +62,7 @@ export class KubernetesTabBaseComponent implements OnInit { ); this.favorite$ = this.kubeEndpointService.endpoint$.pipe( first(), - map(endpoint => this.favoritesConfigMapper.getFavoriteEndpointFromEntity(endpoint.entity)) + map(endpoint => this.userFavoriteManager.getFavoriteEndpointFromEntity(endpoint.entity)) ); this.endpointIds$ = this.kubeEndpointService.endpoint$.pipe( map(endpoint => [endpoint.entity.guid]) diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts index 963c3a0852..364422d36d 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts @@ -20,11 +20,11 @@ import { PaginationPageIteratorConfig, } from '../entity-request-pipeline/pagination-request-base-handlers/pagination-iterator.pipe'; import { EndpointAuthTypeConfig } from '../extension-types'; -import { FavoritesConfigMapper } from '../favorite-config-mapper'; import { EntitySchema } from '../helpers/entity-schema'; import { EndpointModel } from '../types/endpoint.types'; import { StratosStatus } from '../types/shared.types'; import { UserFavorite } from '../types/user-favorites.types'; +import { UserFavoriteManager } from '../user-favorite-manager'; export interface EntityCatalogEntityConfig { entityType: string; @@ -161,7 +161,7 @@ export interface IStratosEndpointDefinition( - entity: any, entityKey: string, favoritesConfigMapper: FavoritesConfigMapper + entity: any, entityKey: string, userFavoriteManager: UserFavoriteManager ) => UserFavorite; /** * Allows the endpoint to fetch user roles, for example when the user loads Stratos or connects an endpoint of this type diff --git a/src/frontend/packages/store/src/favorite-config-mapper.ts b/src/frontend/packages/store/src/favorite-config-mapper.ts deleted file mode 100644 index b34d01bf14..0000000000 --- a/src/frontend/packages/store/src/favorite-config-mapper.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { Injectable } from '@angular/core'; - -import { entityCatalog } from './entity-catalog/entity-catalog'; -import { StratosBaseCatalogEntity } from './entity-catalog/entity-catalog-entity/entity-catalog-entity'; -import { EntityCatalogHelpers } from './entity-catalog/entity-catalog.helper'; -import { IEntityMetadata, IStratosEntityDefinition } from './entity-catalog/entity-catalog.types'; -import { EndpointModel } from './types/endpoint.types'; -import { MenuItem } from './types/menu-item.types'; -import { EntityRequestAction } from './types/request.types'; -import { IFavoriteMetadata, IFavoriteTypeInfo, UserFavorite, UserFavoriteEndpoint } from './types/user-favorites.types'; - -export interface IFavoritesMetaCardConfig { - type: string; - routerLink?: string; - name: string; - menuItems?: MenuItem[]; -} - -export interface IFavoriteConfig { - favoriteInfo: IFavoriteTypeInfo; - prettyName: string; - mapper: TFavoriteMapperFunction; - entityToMetadata: TEntityToMetadata; -} - -export class FavoriteConfig implements IFavoriteConfig { - constructor( - public favoriteInfo: IFavoriteTypeInfo, - public prettyName: string, - public mapper: TFavoriteMapperFunction, - public entityToMetadata: TEntityToMetadata - ) { } -} - -export type TFavoriteMapperFunction = (entity: T) => IFavoritesMetaCardConfig; - -export type TFavoriteActionGenerator = (favorite: UserFavorite) => EntityRequestAction; - -export type TEntityToMetadata = (entity: T) => Q; -export interface IFavoriteActionGenerators { - [key: string]: TFavoriteActionGenerator; -} - -/** - * Stores the config used to hydrator and render favorites. - */ -@Injectable({ - providedIn: 'root' -}) -export class FavoritesConfigMapper { - private mapperKeySeparator = '-'; - constructor() { } - public getMapperKeyFromFavoriteInfo(favoriteInfo: IFavoriteTypeInfo) { - const { endpointType, entityType } = favoriteInfo; - return [endpointType, entityType].join(this.mapperKeySeparator); - } - - /** - * For a given favorite, return the corresponding metadata - */ - public getEntityMetadata(favorite: IFavoriteTypeInfo, entity: any) { - const catalogEntity = entityCatalog.getEntity(favorite.endpointType, favorite.entityType); - return catalogEntity ? catalogEntity.builders.entityBuilder.getMetadata(entity) : null; - } - - private buildFavoriteFromCatalogEntity( - catalogEntity: StratosBaseCatalogEntity, - entity: any, - endpointId: string - ) { - const isEndpoint = catalogEntity.isEndpoint; - const entityDefinition = catalogEntity.definition as IStratosEntityDefinition; - const endpointType = isEndpoint ? catalogEntity.getTypeAndSubtype().type : entityDefinition.endpoint.type; - const entityType = isEndpoint ? EntityCatalogHelpers.endpointType : entityDefinition.type; - const metadata = catalogEntity.builders.entityBuilder.getMetadata(entity); - const guid = isEndpoint ? null : catalogEntity.builders.entityBuilder.getGuid(entity); - if (!endpointId) { - console.error('User favourite - buildFavoriteFromCatalogEntity - endpointId is undefined'); - } - return new UserFavorite( - endpointId, - endpointType, - entityType, - guid, - metadata - ); - } - - public getFavoriteFromEntity( - entityType: string, - endpointType: string, - endpointId: string, - entity: Y - ) { - const catalogEntity = entityCatalog.getEntity(endpointType, entityType) as StratosBaseCatalogEntity; - return this.buildFavoriteFromCatalogEntity(catalogEntity, entity, endpointId); - } - - public getFavoriteEndpointFromEntity( - endpoint: EndpointModel - ): UserFavoriteEndpoint { - return this.getFavoriteFromEntity( - EntityCatalogHelpers.endpointType, - endpoint.cnsi_type, - endpoint.guid, - endpoint - ); - } - -} diff --git a/src/frontend/packages/store/src/user-favorite-helpers.ts b/src/frontend/packages/store/src/user-favorite-helpers.ts index d9601d075d..768c671e79 100644 --- a/src/frontend/packages/store/src/user-favorite-helpers.ts +++ b/src/frontend/packages/store/src/user-favorite-helpers.ts @@ -1,7 +1,7 @@ import { entityCatalog } from './entity-catalog/entity-catalog'; import { IEntityMetadata } from './entity-catalog/entity-catalog.types'; -import { FavoritesConfigMapper } from './favorite-config-mapper'; import { IFavoriteMetadata, UserFavorite } from './types/user-favorites.types'; +import { UserFavoriteManager } from './user-favorite-manager'; export function isEndpointTypeFavorite(favorite: UserFavorite) { return !favorite.entityId; @@ -11,13 +11,13 @@ export function isEndpointTypeFavorite(favorite: UserFavorite export function getFavoriteFromEntity( entity, entityType: string, - favoritesConfigMapper: FavoritesConfigMapper, + userFavoriteManager: UserFavoriteManager, endpointType: string ): UserFavorite { // Use entity catalog to get favorite for the given endpoint type const endpoint = entityCatalog.getEndpoint(endpointType); if (endpoint && endpoint.definition && endpoint.definition.favoriteFromEntity) { - return endpoint.definition.favoriteFromEntity(entity, entityType, favoritesConfigMapper); + return endpoint.definition.favoriteFromEntity(entity, entityType, userFavoriteManager); } return null; diff --git a/src/frontend/packages/store/src/user-favorite-manager.ts b/src/frontend/packages/store/src/user-favorite-manager.ts index 1ca9c5e4b9..7e0e149d54 100644 --- a/src/frontend/packages/store/src/user-favorite-manager.ts +++ b/src/frontend/packages/store/src/user-favorite-manager.ts @@ -4,7 +4,10 @@ import { combineLatest, Observable, of } from 'rxjs'; import { filter, map, switchMap, tap } from 'rxjs/operators'; import { GeneralEntityAppState, IRequestEntityTypeState } from './app-state'; -import { FavoritesConfigMapper } from './favorite-config-mapper'; +import { StratosBaseCatalogEntity } from './entity-catalog/entity-catalog-entity/entity-catalog-entity'; +import { EntityCatalogHelpers } from './entity-catalog/entity-catalog.helper'; +import { IEntityMetadata, IStratosEntityDefinition } from './entity-catalog/entity-catalog.types'; +import { EndpointModel, entityCatalog } from './public-api'; import { endpointEntitiesSelector } from './selectors/endpoint.selectors'; import { errorFetchingFavoritesSelector, @@ -15,7 +18,13 @@ import { import { isFavorite } from './selectors/favorite.selectors'; import { stratosEntityCatalog } from './stratos-entity-catalog'; import { IUserFavoritesGroups } from './types/favorite-groups.types'; -import { IEndpointFavMetadata, IFavoriteMetadata, UserFavorite } from './types/user-favorites.types'; +import { + IEndpointFavMetadata, + IFavoriteMetadata, + IFavoriteTypeInfo, + UserFavorite, + UserFavoriteEndpoint, +} from './types/user-favorites.types'; interface IGroupedFavorites { @@ -27,10 +36,7 @@ interface IGroupedFavorites { providedIn: 'root' }) export class UserFavoriteManager { - constructor( - private store: Store, - private favoritesConfigMapper: FavoritesConfigMapper - ) { } + constructor(private store: Store) { } public getAllFavorites() { const waitForFavorites$ = this.getWaitForFavoritesObservable(); @@ -96,7 +102,7 @@ export class UserFavoriteManager { map(endpoints => { const endpointGuid = UserFavorite.getEntityGuidFromFavoriteGuid(endpointFavoriteGuid); const endpointEntity = endpoints[endpointGuid]; - return this.favoritesConfigMapper.getFavoriteEndpointFromEntity(endpointEntity); + return this.getFavoriteEndpointFromEntity(endpointEntity); }), map(endpointFavorite => ({ endpoint: this.getUserFavoriteFromObject(endpointFavorite), @@ -151,4 +157,57 @@ export class UserFavoriteManager { const idParts = p.slice(0, p.length - 2); return idParts.join('-'); } + + /** + * For a given favorite, return the corresponding metadata + */ + public getEntityMetadata(favorite: IFavoriteTypeInfo, entity: any) { + const catalogEntity = entityCatalog.getEntity(favorite.endpointType, favorite.entityType); + return catalogEntity ? catalogEntity.builders.entityBuilder.getMetadata(entity) : null; + } + + private buildFavoriteFromCatalogEntity( + catalogEntity: StratosBaseCatalogEntity, + entity: any, + endpointId: string + ) { + const isEndpoint = catalogEntity.isEndpoint; + const entityDefinition = catalogEntity.definition as IStratosEntityDefinition; + const endpointType = isEndpoint ? catalogEntity.getTypeAndSubtype().type : entityDefinition.endpoint.type; + const entityType = isEndpoint ? EntityCatalogHelpers.endpointType : entityDefinition.type; + const metadata = catalogEntity.builders.entityBuilder.getMetadata(entity); + const guid = isEndpoint ? null : catalogEntity.builders.entityBuilder.getGuid(entity); + if (!endpointId) { + console.error('User favourite - buildFavoriteFromCatalogEntity - endpointId is undefined'); + } + return new UserFavorite( + endpointId, + endpointType, + entityType, + guid, + metadata + ); + } + + public getFavoriteFromEntity( + entityType: string, + endpointType: string, + endpointId: string, + entity: Y + ) { + const catalogEntity = entityCatalog.getEntity(endpointType, entityType) as StratosBaseCatalogEntity; + return this.buildFavoriteFromCatalogEntity(catalogEntity, entity, endpointId); + } + + public getFavoriteEndpointFromEntity( + endpoint: EndpointModel + ): UserFavoriteEndpoint { + return this.getFavoriteFromEntity( + EntityCatalogHelpers.endpointType, + endpoint.cnsi_type, + endpoint.guid, + endpoint + ); + } + } From 3afeeda659a676a836ea65162f27bba12d88c189 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 9 Nov 2020 15:22:49 +0000 Subject: [PATCH 36/74] Fix unit tests --- .../features/home/cfhome-card/cfhome-card.component.spec.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.spec.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.spec.ts index bd2135b420..87195f96e6 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.spec.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.spec.ts @@ -1,6 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { generateCfBaseTestModules } from '../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { ApplicationDeploySourceTypes } from '../../applications/deploy-application/deploy-application-steps.types'; import { CFHomeCardComponent } from './cfhome-card.component'; describe('CFHomeCardComponent', () => { @@ -10,7 +11,10 @@ describe('CFHomeCardComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ CFHomeCardComponent ], - imports: generateCfBaseTestModules() + imports: generateCfBaseTestModules(), + providers: [ + ApplicationDeploySourceTypes + ] }) .compileComponents(); })); From 69a759877c6c176613c00e106754d0eb398f1202 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 9 Nov 2020 15:37:51 +0000 Subject: [PATCH 37/74] Allow Kubernetes namespaces to be favorited --- .../favorites-meta-card.component.ts | 2 +- .../kubernetes/kubernetes-entity-generator.ts | 26 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts index e7925a8fdb..c5adf81166 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts @@ -62,7 +62,7 @@ export class FavoritesMetaCardComponent { if (!isValid) { const confirmation = new ConfirmationDialogConfig( 'Remove this Favorite?', - `The ${this.favorite.entityType} for this favorite appears to have been deleted. Remove the favorite?`, + `The ${this.favoriteType} for this favorite appears to have been deleted. Remove the favorite?`, 'Remove', true ); diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index 03e0fb1acc..c9800a2025 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -1,6 +1,7 @@ import { Compiler, Injector } from '@angular/core'; import { Validators } from '@angular/forms'; -import { of } from 'rxjs'; +import { OperatorFunction } from 'rxjs'; +import { filter, map, pairwise } from 'rxjs/operators'; import { BaseEndpointAuth } from '../../../core/src/core/endpoint-auth'; import { @@ -29,7 +30,7 @@ import { KubernetesSATokenAuthFormComponent, } from './auth-forms/kubernetes-serviceaccount-auth-form/kubernetes-serviceaccount-auth-form.component'; import { KubeConfigRegistrationComponent } from './kube-config-registration/kube-config-registration.component'; -import { kubeEntityCatalog } from './kubernetes-entity-catalog'; +import { kubeEntityCatalog, kubeEntityCatalog } from './kubernetes-entity-catalog'; import { analysisReportEntityType, KUBERNETES_ENDPOINT_TYPE, @@ -233,7 +234,7 @@ export function generateKubernetesEntities(): StratosBaseCatalogEntity[] { return mod.instance.createHomeCard(mod.componentFactoryResolver); }); }), - fullView: true + fullView: false // shortcuts: k8sShortcuts } }; @@ -310,6 +311,17 @@ function generateNodesEntity(endpointDefinition: StratosEndpointExtensionDefinit return kubeEntityCatalog.node; } + +function entityFetchedWithoutError(): OperatorFunction { + return input$ => input$.pipe( + pairwise(), + filter(([oldV, newV]) => (oldV as any).fetching && !(newV as any).fetching), + map(([, newV]) => newV), + map(f => !(f as any).error) + ) +}; + + function generateNamespacesEntity(endpointDefinition: StratosEndpointExtensionDefinition) { const definition: IStratosEntityDefinition = { type: kubernetesNamespacesEntityType, @@ -323,11 +335,7 @@ function generateNamespacesEntity(endpointDefinition: StratosEndpointExtensionDe definition, { actionBuilders: kubeNamespaceActionBuilders, entityBuilder: { - getIsValid: (favourite) => { - console.log('get is Valid for a namespace'); - console.log(favourite); - return of(false) - }, + getIsValid: (favorite) => kubeEntityCatalog.namespace.api.get(favorite.name, favorite.kubeGuid).pipe(entityFetchedWithoutError()), getMetadata: (namespace: any) => { return { endpointId: namespace.kubeGuid, @@ -336,7 +344,7 @@ function generateNamespacesEntity(endpointDefinition: StratosEndpointExtensionDe name: namespace.metadata.name, }; }, - getLink: metadata => `/kubernetes/${metadata.kubeGuid}/namespaces/${metadata.name}y`, + getLink: metadata => `/kubernetes/${metadata.kubeGuid}/namespaces/${metadata.name}`, getGuid: namespace => namespace.metadata.uid, } }); From 9e51fc300b4d0fda66920faf392bef74d7e99c91 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 9 Nov 2020 15:52:49 +0000 Subject: [PATCH 38/74] Fix duplicate import --- .../kubernetes/src/kubernetes/kubernetes-entity-generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index c9800a2025..34f8807c8e 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -30,7 +30,7 @@ import { KubernetesSATokenAuthFormComponent, } from './auth-forms/kubernetes-serviceaccount-auth-form/kubernetes-serviceaccount-auth-form.component'; import { KubeConfigRegistrationComponent } from './kube-config-registration/kube-config-registration.component'; -import { kubeEntityCatalog, kubeEntityCatalog } from './kubernetes-entity-catalog'; +import { kubeEntityCatalog } from './kubernetes-entity-catalog'; import { analysisReportEntityType, KUBERNETES_ENDPOINT_TYPE, From 83173cb36df207bd91103740dc3489065bbd7d12 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 9 Nov 2020 20:00:05 +0000 Subject: [PATCH 39/74] Tidy ups --- .../cloud-foundry/src/cf-entity-generator.ts | 14 ++------------ .../cloud-foundry/src/cf-metadata-types.ts | 1 - .../store/src/types/user-favorites.types.ts | 3 +-- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index 9c7838b884..e74b64d0df 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -1,6 +1,5 @@ import { Compiler, Injector } from '@angular/core'; import { Action, Store } from '@ngrx/store'; -import moment from 'moment'; import { combineLatest, Observable, of, OperatorFunction } from 'rxjs'; import { filter, first, map, pairwise } from 'rxjs/operators'; @@ -443,6 +442,7 @@ export function generateCFEntities(): StratosBaseCatalogEntity[] { ]; } +// Helper operator function entityFetchedWithoutError(): OperatorFunction { return input$ => input$.pipe( pairwise(), @@ -1214,7 +1214,6 @@ function generateCfApplicationEntity(endpointDefinition: StratosEndpointExtensio getMetadata: app => ({ guid: app.metadata.guid, cfGuid: app.entity.cfGuid, - createdAt: moment(app.metadata.created_at).format('LLL'), name: app.entity.name, }), getLink: (metadata) => `/applications/${metadata.cfGuid}/${metadata.guid}/summary`, @@ -1255,7 +1254,6 @@ function generateCfSpaceEntity(endpointDefinition: StratosEndpointExtensionDefin orgGuid: space.entity.organization_guid ? space.entity.organization_guid : space.entity.organization.metadata.guid, name: space.entity.name, cfGuid: space.entity.cfGuid, - createdAt: moment(space.metadata.created_at).format('LLL'), }), getLink: metadata => `/cloud-foundry/${metadata.cfGuid}/organizations/${metadata.orgGuid}/spaces/${metadata.guid}/summary`, getGuid: entity => entity.metadata.guid, @@ -1293,26 +1291,18 @@ function generateCfOrgEntity(endpointDefinition: StratosEndpointExtensionDefinit entityBuilder: { getMetadata: org => ({ guid: org.metadata.guid, - status: getOrgStatus(org), name: org.entity.name, cfGuid: org.entity.cfGuid, - createdAt: moment(org.metadata.created_at).format('LLL'), }), getLink: metadata => `/cloud-foundry/${metadata.cfGuid}/organizations/${metadata.guid}`, getGuid: entity => entity.metadata.guid, + getIsValid: (metadata) => cfEntityCatalog.org.api.get(metadata.guid, metadata.cfGuid, {}).pipe(entityFetchedWithoutError()) } } ); return cfEntityCatalog.org; } -function getOrgStatus(org: APIResource) { - if (!org || !org.entity || !org.entity.status) { - return 'Unknown'; - } - return org.entity.status.charAt(0).toUpperCase() + org.entity.status.slice(1); -} - function generateCFMetrics(endpointDefinition: StratosEndpointExtensionDefinition) { const definition: IStratosEntityDefinition = { type: metricEntityType, diff --git a/src/frontend/packages/cloud-foundry/src/cf-metadata-types.ts b/src/frontend/packages/cloud-foundry/src/cf-metadata-types.ts index 5103014d69..7f2889580c 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-metadata-types.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-metadata-types.ts @@ -9,7 +9,6 @@ export interface ISpaceFavMetadata extends IFavoriteMetadata { export interface IOrgFavMetadata extends IFavoriteMetadata { guid: string; - status: string; name: string; cfGuid: string; } diff --git a/src/frontend/packages/store/src/types/user-favorites.types.ts b/src/frontend/packages/store/src/types/user-favorites.types.ts index ddcc43cfc9..29a9658d1c 100644 --- a/src/frontend/packages/store/src/types/user-favorites.types.ts +++ b/src/frontend/packages/store/src/types/user-favorites.types.ts @@ -90,8 +90,7 @@ export class UserFavorite implement public canFavorite(): boolean { // What do we need to be able to favorite an entity? - // TODO - return true; + return !!this.entityBuilder.getGuid && !!this.entityBuilder.getMetadata && !!this.entityBuilder.getLink; } // Get the link to navigate to the view for the given entity backing this user favorite From 4e67ee99ac913bd9e4a359640157f0c974bd9883 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 10 Nov 2020 15:20:59 +0000 Subject: [PATCH 40/74] e2e Debugging --- src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts b/src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts index 7ce53760c4..e4b822c46b 100644 --- a/src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts @@ -34,6 +34,9 @@ describe('Delete Organization', () => { return cfHelper.baseAddOrg(cfGuid, orgName).then(org => { orgGuid = org.metadata.guid; return cfHelper.baseAddSpace(cfGuid, orgGuid, spaceName); + }).catch(e => { + console.error('Failed to create org/space'); + console.error(e); }); }); }); From ac7858de4a48490a1941b8d53ac47a7ebaf5184a Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 10 Nov 2020 15:25:52 +0000 Subject: [PATCH 41/74] Fix for org delete e2e test --- src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts b/src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts index e4b822c46b..7c5ab1f173 100644 --- a/src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts @@ -4,6 +4,7 @@ import { e2e } from '../../e2e'; import { CFHelpers } from '../../helpers/cf-e2e-helpers'; import { ConsoleUserType, E2EHelpers } from '../../helpers/e2e-helpers'; import { CFPage } from '../../po/cf-page.po'; +import { ListComponent } from '../../po/list.po'; import { SideNavMenuItem } from '../../po/side-nav.po'; import { CfTopLevelPage } from '../cf-level/cf-top-level-page.po'; @@ -54,6 +55,8 @@ describe('Delete Organization', () => { cfPage.waitForPageOrChildPage(); cfPage.loadingIndicator.waitUntilNotShown(); cfPage.goToOrgTab(); + const list = new ListComponent(); + list.header.refresh(); cfPage.deleteOrg(orgName); expect(element(by.tagName('app-cards')).getText()).not.toContain(orgName); }); From 45782404cc9403755a7d7b167952ca5ba7dd96c9 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 10 Nov 2020 17:07:29 +0000 Subject: [PATCH 42/74] E2e Test fix --- src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts | 3 --- .../cloud-foundry/space-level/cf-space-delete-e2e.spec.ts | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts b/src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts index 7c5ab1f173..b22d92c86c 100644 --- a/src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/org-level/cf-org-delete-e2e.spec.ts @@ -35,9 +35,6 @@ describe('Delete Organization', () => { return cfHelper.baseAddOrg(cfGuid, orgName).then(org => { orgGuid = org.metadata.guid; return cfHelper.baseAddSpace(cfGuid, orgGuid, spaceName); - }).catch(e => { - console.error('Failed to create org/space'); - console.error(e); }); }); }); diff --git a/src/test-e2e/cloud-foundry/space-level/cf-space-delete-e2e.spec.ts b/src/test-e2e/cloud-foundry/space-level/cf-space-delete-e2e.spec.ts index c6dc55efbc..2fe4c6f079 100644 --- a/src/test-e2e/cloud-foundry/space-level/cf-space-delete-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/space-level/cf-space-delete-e2e.spec.ts @@ -62,6 +62,7 @@ describe('Delete Space', () => { // Find the Org and click on it const list = new ListComponent(); + list.header.refresh(); return list.cards.findCardByTitle(orgName, MetaCardTitleType.CUSTOM, true).then(card => { expect(card).toBeDefined(); card.click(); From 859316f33116e0184e75b7c1ebc3a52ad1c8ae8b Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 10 Nov 2020 17:27:29 +0000 Subject: [PATCH 43/74] Refactor out common entity operator --- .../cloud-foundry/src/cf-entity-generator.ts | 15 +++------------ .../src/kubernetes/kubernetes-entity-generator.ts | 14 +------------- src/frontend/packages/store/src/operators.ts | 15 +++++++++++++++ src/frontend/packages/store/src/public-api.ts | 3 +++ 4 files changed, 22 insertions(+), 25 deletions(-) create mode 100644 src/frontend/packages/store/src/operators.ts diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index e74b64d0df..8b5961fa73 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -1,7 +1,8 @@ import { Compiler, Injector } from '@angular/core'; import { Action, Store } from '@ngrx/store'; -import { combineLatest, Observable, of, OperatorFunction } from 'rxjs'; -import { filter, first, map, pairwise } from 'rxjs/operators'; +import { entityFetchedWithoutError } from '@stratosui/store'; +import { combineLatest, Observable, of } from 'rxjs'; +import { first, map } from 'rxjs/operators'; import { BaseEndpointAuth } from '../../core/src/core/endpoint-auth'; import { urlValidationExpression } from '../../core/src/core/utils.service'; @@ -442,16 +443,6 @@ export function generateCFEntities(): StratosBaseCatalogEntity[] { ]; } -// Helper operator -function entityFetchedWithoutError(): OperatorFunction { - return input$ => input$.pipe( - pairwise(), - filter(([oldV, newV]) => (oldV as any).fetching && !(newV as any).fetching), - map(([, newV]) => newV), - map(f => !(f as any).error) - ) -}; - function generateCFQuotaDefinitionEntity(endpointDefinition: StratosEndpointExtensionDefinition) { const definition: IStratosEntityDefinition = { type: quotaDefinitionEntityType, diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index 34f8807c8e..89911f6ff4 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -1,7 +1,6 @@ import { Compiler, Injector } from '@angular/core'; import { Validators } from '@angular/forms'; -import { OperatorFunction } from 'rxjs'; -import { filter, map, pairwise } from 'rxjs/operators'; +import { entityFetchedWithoutError } from '@stratosui/store'; import { BaseEndpointAuth } from '../../../core/src/core/endpoint-auth'; import { @@ -311,17 +310,6 @@ function generateNodesEntity(endpointDefinition: StratosEndpointExtensionDefinit return kubeEntityCatalog.node; } - -function entityFetchedWithoutError(): OperatorFunction { - return input$ => input$.pipe( - pairwise(), - filter(([oldV, newV]) => (oldV as any).fetching && !(newV as any).fetching), - map(([, newV]) => newV), - map(f => !(f as any).error) - ) -}; - - function generateNamespacesEntity(endpointDefinition: StratosEndpointExtensionDefinition) { const definition: IStratosEntityDefinition = { type: kubernetesNamespacesEntityType, diff --git a/src/frontend/packages/store/src/operators.ts b/src/frontend/packages/store/src/operators.ts new file mode 100644 index 0000000000..e490ab161e --- /dev/null +++ b/src/frontend/packages/store/src/operators.ts @@ -0,0 +1,15 @@ +import { OperatorFunction } from 'rxjs'; +import { filter, map, pairwise } from 'rxjs/operators'; + +// Helper operators + +// Monitors an entity fetch operation and generates a single boolean to +// indicate if the fetch succeeded without an error +export function entityFetchedWithoutError(): OperatorFunction { + return input$ => input$.pipe( + pairwise(), + filter(([oldV, newV]) => (oldV as any).fetching && !(newV as any).fetching), + map(([, newV]) => newV), + map(f => !(f as any).error) + ) +}; diff --git a/src/frontend/packages/store/src/public-api.ts b/src/frontend/packages/store/src/public-api.ts index ea38cf610c..f355a7ee1a 100644 --- a/src/frontend/packages/store/src/public-api.ts +++ b/src/frontend/packages/store/src/public-api.ts @@ -24,3 +24,6 @@ export { BaseEntityValues } from './types/entity.types'; export { WrapperRequestActionSuccess } from './types/request.types'; export { flattenPagination, PaginationFlattener } from './helpers/paginated-request-helpers'; + +// Operators +export { entityFetchedWithoutError } from './operators'; \ No newline at end of file From 1fa7d8cf64e9963d3aa6ab6863ab82d054535772 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 10 Nov 2020 17:30:56 +0000 Subject: [PATCH 44/74] Slight refactor --- src/frontend/packages/store/src/user-favorite-manager.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/frontend/packages/store/src/user-favorite-manager.ts b/src/frontend/packages/store/src/user-favorite-manager.ts index 7e0e149d54..76c483b2d5 100644 --- a/src/frontend/packages/store/src/user-favorite-manager.ts +++ b/src/frontend/packages/store/src/user-favorite-manager.ts @@ -65,10 +65,6 @@ export class UserFavoriteManager { } public hydrateAllFavorites(): Observable { - return this.getHydrateObservable(); - } - - private getHydrateObservable() { return this.getAllFavorites().pipe( filter(([groups, favoriteEntities]) => !!groups && !!favoriteEntities), switchMap(([groups, favoriteEntities]) => this.getHydratedGroups(groups, favoriteEntities)) @@ -120,10 +116,6 @@ export class UserFavoriteManager { return new UserFavorite(f.endpointId, f.endpointType, f.entityType, f.entityId, f.metadata); } - public hydrateFavorite(favorite: UserFavorite): IFavoriteMetadata { - return favorite.metadata; - } - public getIsFavoriteObservable(favorite: UserFavorite) { return this.store.select( isFavorite(favorite) From 7a030f4e8fde3ec3df5e2d264afb733c0566f5ac Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 10 Nov 2020 20:03:08 +0000 Subject: [PATCH 45/74] Fix frontend unit tests --- .../components/list/simple-list/simple-list.component.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/packages/core/src/shared/components/list/simple-list/simple-list.component.spec.ts b/src/frontend/packages/core/src/shared/components/list/simple-list/simple-list.component.spec.ts index a3edaef193..c34d18b826 100644 --- a/src/frontend/packages/core/src/shared/components/list/simple-list/simple-list.component.spec.ts +++ b/src/frontend/packages/core/src/shared/components/list/simple-list/simple-list.component.spec.ts @@ -35,7 +35,6 @@ describe('SimpleListComponent', () => { endpoint, }, { entityBuilder: { - getLines: () => ([]), getMetadata: () => ({ name: 'test' }), getGuid: () => 'test', }, From dcb84a88927bfbc451af81d23bb75fee95b0354e Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 13 Nov 2020 13:54:30 +0000 Subject: [PATCH 46/74] Address PR feedback so far --- .../services/cloud-foundry-endpoint.service.ts | 16 +++++++--------- .../home/cfhome-card/cfhome-card.component.ts | 9 ++------- .../dashboard-base/dashboard-base.component.html | 4 ++-- .../dashboard-base/dashboard-base.component.ts | 8 +------- .../core/src/features/home/home.types.ts | 11 ----------- .../tile-selector-tile.component.scss | 2 -- .../src/shared/services/side-panel.service.ts | 4 ++-- .../kubernetes-namespace.component.ts | 1 - 8 files changed, 14 insertions(+), 41 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/services/cloud-foundry-endpoint.service.ts b/src/frontend/packages/cloud-foundry/src/features/cf/services/cloud-foundry-endpoint.service.ts index bba49f0208..a87a0f7173 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/services/cloud-foundry-endpoint.service.ts +++ b/src/frontend/packages/cloud-foundry/src/features/cf/services/cloud-foundry-endpoint.service.ts @@ -134,6 +134,12 @@ export class CloudFoundryEndpointService { return fetchTotalResults(action, store, pmf); } + // Fetch the cound of organisations in a Cloud Foundry + public static fetchOrgCount(store: Store, pmf: PaginationMonitorFactory, cfGuid: string): Observable { + const getAllOrgsAction = CloudFoundryEndpointService.createGetAllOrganizations(cfGuid); + return fetchTotalResults(getAllOrgsAction, store, pmf); + } + public static fetchOrgs(store: Store, pmf: PaginationMonitorFactory, cfGuid: string): Observable[]> { const getAllOrgsAction = CloudFoundryEndpointService.createGetAllOrganizations(cfGuid); @@ -154,15 +160,7 @@ export class CloudFoundryEndpointService { private cfUserService: CfUserService, private pmf: PaginationMonitorFactory, ) { - if (this.activeRouteCfOrgSpace) { - this.init(this.activeRouteCfOrgSpace); - } - } - - public init(config: ActiveRouteCfOrgSpace) { - this.activeRouteCfOrgSpace = config; - this.cfGuid = this.activeRouteCfOrgSpace.cfGuid; - + this.cfGuid = activeRouteCfOrgSpace.cfGuid; this.cfEndpointEntityService = stratosEntityCatalog.endpoint.store.getEntityService(this.cfGuid); this.cfInfoEntityService = cfEntityCatalog.cfInfo.store.getEntityService(this.cfGuid) diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts index e4a95dbc6f..2cb876fd92 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts @@ -124,7 +124,7 @@ export class CFHomeCardComponent { this.cardLoaded = true; this.routeCount$ = CloudFoundryEndpointService.fetchRouteCount(this.store, this.pmf, this.guid); this.appCount$ = CloudFoundryEndpointService.fetchAppCount(this.store, this.pmf, this.guid); - this.orgCount$ = CloudFoundryEndpointService.fetchOrgs(this.store, this.pmf, this.guid).pipe(map(orgs => orgs.length)); + this.orgCount$ = CloudFoundryEndpointService.fetchOrgCount(this.store, this.pmf, this.guid); this.appLink = () => goToAppWall(this.store, this.guid);; @@ -182,11 +182,9 @@ export class CFHomeCardComponent { first() ).subscribe(a => { this.fetchAppStats(); - this.fetchAppStats(); }); } else { this.fetchAppStats(); - this.fetchAppStats(); } } else { this.appStatsLoaded.next(true); @@ -194,10 +192,7 @@ export class CFHomeCardComponent { } private restrictApps(apps: APIResource[]): APIResource[] { - if (!apps) { - return []; - } - return [...apps.sort(appDataSort).slice(0, this.recentAppsRows)]; + return !apps ? [] :[...apps.sort(appDataSort).slice(0, this.recentAppsRows)]; } } diff --git a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html index d2210b8c3f..8f1388b896 100644 --- a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html +++ b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.html @@ -42,10 +42,10 @@
+ [ngClass]="{'dashboard__side-narrow': (sidePanelService.previewMode$ | async) === 2}">
diff --git a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.ts b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.ts index 66be597449..1bf92e5dac 100644 --- a/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.ts +++ b/src/frontend/packages/core/src/features/dashboard/dashboard-base/dashboard-base.component.ts @@ -42,7 +42,6 @@ export class DashboardBaseComponent implements OnInit, OnDestroy, AfterViewInit public noMargin$: Observable; private closeSub: Subscription; private mobileSub: Subscription; - private sidePanelSub: Subscription; private drawer: MatDrawer; public iconModeOpen = false; public sideNavWidth = 54; @@ -55,7 +54,7 @@ export class DashboardBaseComponent implements OnInit, OnDestroy, AfterViewInit @ViewChild('content') public content; // Slide-in side panel mode - sidePanelMode: SidePanelMode = 0; + sidePanelMode: SidePanelMode = SidePanelMode.Modal; constructor( public pageHeaderService: PageHeaderService, @@ -98,10 +97,6 @@ export class DashboardBaseComponent implements OnInit, OnDestroy, AfterViewInit }) ); - this.sidePanelSub = this.sidePanelService.previewMode$.subscribe(mode => { - this.sidePanelMode = mode - }); - this.mobileSub = this.isMobile$ .subscribe(isMobile => isMobile ? this.store.dispatch(new EnableMobileNav()) : this.store.dispatch(new DisableMobileNav())); } @@ -162,7 +157,6 @@ export class DashboardBaseComponent implements OnInit, OnDestroy, AfterViewInit this.mobileSub.unsubscribe(); this.closeSub.unsubscribe(); this.sidePanelService.unsetContainer(); - this.sidePanelSub.unsubscribe(); } isNoMarginView(route: ActivatedRouteSnapshot): boolean { diff --git a/src/frontend/packages/core/src/features/home/home.types.ts b/src/frontend/packages/core/src/features/home/home.types.ts index 3a5d9f0119..0e8d24f76d 100644 --- a/src/frontend/packages/core/src/features/home/home.types.ts +++ b/src/frontend/packages/core/src/features/home/home.types.ts @@ -13,17 +13,6 @@ export class HomePageCardLayout { constructor(public x: number, public y: number, public title?: string) { this.id = x + y * 1000; } - - public static fromLayout(layout: string, title?: string): HomePageCardLayout { - const parts = layout.split('-'); - const x = parseInt(parts[0], 10); - let y = 1; - if (parts.length > 1) { - y = parseInt(parts[1], 10); - } - - return new HomePageCardLayout(x, y, title ? title : `${x}-${y} Layout`); - } } export abstract class HomePageEndpointCard { diff --git a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.scss b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.scss index d467bec4f8..3eaf453929 100644 --- a/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.scss +++ b/src/frontend/packages/core/src/shared/components/tile-selector-tile/tile-selector-tile.component.scss @@ -87,6 +87,4 @@ margin: 10px; } } - - } diff --git a/src/frontend/packages/core/src/shared/services/side-panel.service.ts b/src/frontend/packages/core/src/shared/services/side-panel.service.ts index 8ee7c87d02..c58dac3de2 100644 --- a/src/frontend/packages/core/src/shared/services/side-panel.service.ts +++ b/src/frontend/packages/core/src/shared/services/side-panel.service.ts @@ -13,7 +13,7 @@ import { filter, observeOn, publishReplay, refCount, tap } from 'rxjs/operators' // Side Panel Modes export enum SidePanelMode { - // Modal = spans the full height of the window and overlaps the top bat + // Modal = spans the full height of the window and overlaps the title bar Modal = 0, // Normal = 600px width and height not overlapping title bar Normal = 1, @@ -65,7 +65,7 @@ export class SidePanelService { } /** - * Show the preview panel in the given mode - does not overlap title bar and colours are more muted + * Show the preview panel in the given mode */ public showMode( mode: SidePanelMode, component: object, props?: { [key: string]: any }, componentFactoryResolver?: ComponentFactoryResolver) { diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts index 5bd6b02238..fe8ecff185 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts @@ -66,7 +66,6 @@ export class KubernetesNamespaceComponent { } public favorite$ = this.kubeNamespaceService.namespace$.pipe( - // tap(a => console.log(a)), filter(app => !!app), map(namespace => getFavoriteFromEntity( { From a3f98d3cacb4e65a647eff4b24c388eb8a447e89 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 13 Nov 2020 17:00:51 +0000 Subject: [PATCH 47/74] Address PR feedback --- .../cloud-foundry/src/cf-entity-generator.ts | 2 +- .../home/cfhome-card/cfhome-card.component.ts | 4 +- .../core/src/features/home/home.types.ts | 3 +- .../home-page-endpoint-card.component.ts | 43 +++++++------------ .../features/home/home/home-page.component.ts | 11 +++-- .../kubernetes/kubernetes-entity-generator.ts | 8 ---- .../services/kubernetes-namespace.service.ts | 9 +--- .../entity-catalog/entity-catalog.types.ts | 11 ++--- 8 files changed, 31 insertions(+), 60 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index e91d2aa5b4..41bb31d24e 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -275,7 +275,7 @@ export function generateCFEntities(): StratosBaseCatalogEntity[] { logoUrl: '/core/assets/endpoint-icons/cloudfoundry.png', authTypes: [BaseEndpointAuth.UsernamePassword, BaseEndpointAuth.SSO], homeCard: { - component: (compiler: Compiler, injector: Injector) => import('./features/home/cfhome-card/cfhome-card.module').then((m) => { + component: (compiler: Compiler, injector: Injector) => import('./features/home/cfhome-card/cfhome-card.module').then(m => { return compiler.compileModuleAndAllComponentsAsync(m.CFHomeCardModule).then(cm => { const mod = cm.ngModuleFactory.create(injector); return mod.instance.createHomeCard(mod.componentFactoryResolver); diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts index 2cb876fd92..4c522f6129 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts @@ -24,7 +24,7 @@ import { import { ActiveRouteCfOrgSpace } from '../../cf/cf-page.types'; import { goToAppWall } from '../../cf/cf.helpers'; import { appDataSort, CloudFoundryEndpointService } from '../../cf/services/cloud-foundry-endpoint.service'; -import { HomePageCardLayout } from './../../../../../core/src/features/home/home.types'; +import { HomePageCardLayout, HomePageEndpointCard } from './../../../../../core/src/features/home/home.types'; import { ITileConfig } from './../../../../../core/src/shared/components/tile/tile-selector.types'; @@ -40,7 +40,7 @@ import { ITileConfig } from './../../../../../core/src/shared/components/tile/ti CloudFoundryEndpointService ] }) -export class CFHomeCardComponent { +export class CFHomeCardComponent implements HomePageEndpointCard { _layout: HomePageCardLayout; diff --git a/src/frontend/packages/core/src/features/home/home.types.ts b/src/frontend/packages/core/src/features/home/home.types.ts index 0e8d24f76d..2e60d1afc8 100644 --- a/src/frontend/packages/core/src/features/home/home.types.ts +++ b/src/frontend/packages/core/src/features/home/home.types.ts @@ -2,6 +2,7 @@ import { Observable } from 'rxjs'; import { HomeCardShortcut } from '../../../../store/src/entity-catalog/entity-catalog.types'; import { EndpointModel } from '../../../../store/src/public-api'; +import { IHydrationResults } from '../../../../store/src/types/user-favorite-manager.types'; // Layout for a home page card @@ -22,7 +23,7 @@ export abstract class HomePageEndpointCard { } export interface LinkMetadata { - favs: any[], + favs: IHydrationResults[], shortcuts: HomeCardShortcut[] } diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index 627190ba82..7e2134242e 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -17,7 +17,6 @@ import { filter, first, map, timeout } from 'rxjs/operators'; import { EntityCatalogSchemas, - HomeCardShortcut, IStratosEndpointDefinition, } from '../../../../../../store/src/entity-catalog/entity-catalog.types'; import { FavoritesConfigMapper } from '../../../../../../store/src/favorite-config-mapper'; @@ -62,8 +61,6 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi favorites$: Observable; - shortcuts: HomeCardShortcut[]; - layout$ = new BehaviorSubject(null); links$: Observable; @@ -116,22 +113,14 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi ngOnInit() { // Favorites for this endpoint this.favorites$ = this.userFavoriteManager.getFavoritesForEndpoint(this.endpoint.guid).pipe( - map(f => { - return f.map(item => this.userFavoriteManager.mapToHydrated(item)); - }) + map(f => f.map(item => this.userFavoriteManager.mapToHydrated(item))) ); this.entity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type) if (this.entity) { this.definition = this.entity.definition; this.favorite = this.favoritesConfigMapper.getFavoriteEndpointFromEntity(this.endpoint); - - // Get the list of shortcuts for the endpoint for the given endpoint ID - if (this.definition.homeCard && this.definition.homeCard.shortcuts) { - this.shortcuts = this.definition.homeCard.shortcuts(this.endpoint.guid); - } - - this.fullView = this.definition.homeCard && this.definition.homeCard.fullView; + this.fullView = this.definition?.homeCard?.fullView; const mapper = this.favoritesConfigMapper.getMapperFunction(this.favorite); if (mapper && this.favorite.metadata) { @@ -145,10 +134,11 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi this.links$ = combineLatest([this.favorites$, this.layout$.asObservable()]).pipe( filter(([favs, layout]) => !!layout), map(([favs, layout]) => { - let shortcuts: HomeCardShortcut[] = this.shortcuts || []; - + // Get the list of shortcuts for the endpoint for the given endpoint ID + const allShortcuts = this.definition?.homeCard?.shortcuts(this.endpoint.guid) || []; + let shortcuts = allShortcuts; const max = (layout.y > 1) ? MAX_FAVS_COMPACT : MAX_FAVS_NORMAL; - const totalShortcuts = shortcuts.length; + const totalShortcuts = allShortcuts.length; this.hiddenFavorites = favs.length - max; // Based on the layout, adjust the numbers returned @@ -159,7 +149,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi favs = favs.slice(0, max); } if (totalShortcuts > MAX_SHORTCUTS) { - shortcuts = this.shortcuts.slice(0, MAX_SHORTCUTS); + shortcuts = allShortcuts.slice(0, MAX_SHORTCUTS); } // We only want to display 5 things if (favs.length + totalShortcuts > MAX_LINKS) { @@ -167,7 +157,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi if (limit === 1) { limit = 0; } - shortcuts = this.shortcuts.slice(0, limit); + shortcuts = allShortcuts.slice(0, limit); } } else { // Full card view - move the shortcuts into the main left panel if we have more @@ -196,9 +186,8 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi // Layout has changed public updateLayout() { this.layout$.next(this.layout); - if (this.ref && this.ref.instance) { - (this.ref.instance as any).layout = this._layout; + this.ref.instance.layout = this._layout; } } @@ -206,25 +195,25 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi this.customCard.clear(); const component = await endpointEntity.definition.homeCard.component(this.compiler, this.injector); this.ref = this.customCard.createComponent(component); - (this.ref.instance as any).endpoint = this.endpoint; - (this.ref.instance as any).layout = this._layout; - this.loadCard(); + this.ref.instance.endpoint = this.endpoint; + this.ref.instance.layout = this._layout; + this.loadCardIfReady(); } // Load the card public load() { this.canLoad = true; - this.loadCard(); + this.loadCardIfReady(); } // Ask the card to load itself - loadCard() { + loadCardIfReady() { if (this.canLoad && this.ref && this.ref.instance && this.ref.instance.load) { this.isLoading = true; const loadObs = this.ref.instance.load() || of(true); - // Timeout after 10 seconds - this.sub = loadObs.pipe(timeout(10000), filter(v => v === true), first()).subscribe(() => { + // Timeout after 15 seconds + this.sub = loadObs.pipe(timeout(15000), filter(v => v === true), first()).subscribe(() => { this.loaded.next(); this.isLoading = false; }, () => { diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.ts index 6a5e368311..d4e47b740a 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.ts @@ -60,7 +60,6 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { ]; @ViewChild('endpointsPanel') endpointsPanel; - @ViewChildren(HomePageEndpointCardComponent) endpointCards: QueryList; @ViewChildren('endpointCard') endpointElements: QueryList; @@ -68,7 +67,7 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { cardsToLoad: HomePageEndpointCardComponent[] = []; isLoadingACard = false; - private sub: Subscription; + private viewMonitorSub: Subscription; private cardChangesSub: Subscription; private checkLayout = new BehaviorSubject(true); @@ -136,7 +135,7 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { }), startWith(0)); // Load cards as they come into view - this.sub = combineLatest([scroll$, check$]).pipe(debounceTime(200)).subscribe(([scrollTop, check]) => { + this.viewMonitorSub = combineLatest([scroll$, check$]).pipe(debounceTime(200)).subscribe(([scrollTop, check]) => { // User has scrolled - check the remaining cards that have not been loaded to see if any are now visible and shoule be loaded // Only load the first one - after that one has loaded, we'll call this method again and check for the next one const remaining = []; @@ -151,11 +150,11 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { if ((cardTop >= scrollTop && cardTop <= scrollBottom) || (cardBottom >= scrollTop && cardBottom <= scrollBottom)) { const card = this.endpointCards.toArray()[index]; this.cardsToLoad.push(card); - this.processCardsToLoad(); } else { remaining.push(index); } }; + this.processCardsToLoad(); this.notLoadedCardIndices = remaining; }) } @@ -169,8 +168,8 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { } ngOnDestroy() { - if (this.sub) { - this.sub.unsubscribe(); + if (this.viewMonitorSub) { + this.viewMonitorSub.unsubscribe(); } if (this.cardChangesSub) { this.cardChangesSub.unsubscribe(); diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index 71c729afba..ecbc803a2e 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -1,6 +1,5 @@ import { Compiler, Injector } from '@angular/core'; import { Validators } from '@angular/forms'; -import { of } from 'rxjs'; import { BaseEndpointAuth } from '../../../core/src/core/endpoint-auth'; import { @@ -323,11 +322,6 @@ function generateNamespacesEntity(endpointDefinition: StratosEndpointExtensionDe definition, { actionBuilders: kubeNamespaceActionBuilders, entityBuilder: { - getIsValid: (favourite) => { - console.log('get is Valid for a namespace'); - console.log(favourite); - return of(false) - }, getMetadata: (namespace: any) => { return { endpointId: namespace.kubeGuid, @@ -395,8 +389,6 @@ function getFavoriteFromKubeEntity( entityType: string, favoritesConfigMapper: FavoritesConfigMapper ): UserFavorite { - console.log('Hello'); - console.log(entity); return favoritesConfigMapper.getFavoriteFromEntity( entityType, KUBERNETES_ENDPOINT_TYPE, diff --git a/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-namespace.service.ts b/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-namespace.service.ts index a86b1e2f19..ef31dc8889 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-namespace.service.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-namespace.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; +import { map } from 'rxjs/operators'; import { getIdFromRoute } from '../../../../core/src/core/utils.service'; import { kubeEntityCatalog } from '../kubernetes-entity-catalog'; @@ -23,11 +23,6 @@ export class KubernetesNamespaceService { this.kubeGuid = kubeEndpointService.kubeGuid; const namespaceEntity = kubeEntityCatalog.namespace.store.getEntityService(this.namespaceName, this.kubeGuid); - - this.namespace$ = namespaceEntity.entityObs$.pipe( - filter(p => !!p && !!p.entity), - map(p => p.entity), - ); - + this.namespace$ = namespaceEntity.waitForEntity$.pipe(map(e => e.entity)); } } diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts index 6ae0bdfff7..ac1571cb94 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts @@ -1,7 +1,8 @@ -import { Compiler, Injector } from '@angular/core'; +import { Compiler, ComponentFactory, Injector } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; +import { HomePageEndpointCard } from '../../../core/src/features/home/home.types'; import { IListAction } from '../../../core/src/shared/components/list/list.component.types'; import { AppState, GeneralEntityAppState } from '../app-state'; import { @@ -70,7 +71,7 @@ export interface HomeCardShortcut { // Metadata for Home Card export interface HomeCardMetadata { - component?: (compiler: Compiler, injector: Injector) => any; + component?: (compiler: Compiler, injector: Injector) => Promise>; shortcuts?: (endpointID: string) => HomeCardShortcut[]; fullView?: boolean; } @@ -229,7 +230,6 @@ export type EntityRowBuilder = [string, (entity: T, store?: Store { getMetadata(entity: Y): T; - getStatusObservable?(entity: Y): Observable; // TODO This should be used in the entities schema. getGuid(entityMetadata: T): string; getLink?(entityMetadata: T): string; @@ -238,11 +238,6 @@ export interface IStratosEntityBuilder { singular: string, plural: string, }; - /** - * Checks if the given entity is stil valid (e.g. has not been deleted) - * @param entityMetadata Entity metadata - */ - getIsValid?(entityMetadata: T): Observable; /** * Actions that don't effect an individual entity i.e. create new * @returns global actions From 3084f68cc5a93da24946f70ca9959ec900562a6b Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 13 Nov 2020 17:24:22 +0000 Subject: [PATCH 48/74] Fix connected endpoint state check on home screen --- .../packages/core/src/core/endpoints.service.ts | 14 +++++++------- .../src/features/home/home/home-page.component.ts | 14 ++++++-------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/frontend/packages/core/src/core/endpoints.service.ts b/src/frontend/packages/core/src/core/endpoints.service.ts index 8da0f1d610..6d61ef7af4 100644 --- a/src/frontend/packages/core/src/core/endpoints.service.ts +++ b/src/frontend/packages/core/src/core/endpoints.service.ts @@ -24,6 +24,7 @@ export class EndpointsService implements CanActivate { haveRegistered$: Observable; haveConnected$: Observable; disablePersistenceFeatures$: Observable; + connectedEndpoints$: Observable; static getLinkForEndpoint(endpoint: EndpointModel): string { if (!endpoint) { @@ -44,18 +45,17 @@ export class EndpointsService implements CanActivate { ) { this.endpoints$ = store.select(endpointEntitiesSelector); this.haveRegistered$ = this.endpoints$.pipe(map(endpoints => !!Object.keys(endpoints).length)); - this.haveConnected$ = this.endpoints$.pipe(map(endpoints => - !!Object.values(endpoints).find(endpoint => { + this.connectedEndpoints$ = this.endpoints$.pipe(map(endpoints => + Object.values(endpoints).filter(endpoint => { const epType = entityCatalog.getEndpoint(endpoint.cnsi_type, endpoint.sub_type); if (!epType || !epType.definition) { return false; } const epEntity = epType.definition; - return epEntity.unConnectable || - endpoint.connectionStatus === 'connected' || - endpoint.connectionStatus === 'checking'; - })) - ); + return epEntity.unConnectable || endpoint.connectionStatus === 'connected' || endpoint.connectionStatus === 'checking'; + }) + )); + this.haveConnected$ = this.connectedEndpoints$.pipe(map(endpoints => endpoints.length > 0)); this.disablePersistenceFeatures$ = this.store.select('auth').pipe( map((auth) => auth.sessionData && diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.ts index d4e47b740a..8b44305217 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.ts @@ -18,7 +18,7 @@ import { debounceTime, filter, first, map, startWith } from 'rxjs/operators'; import { SetHomeCardLayoutAction } from '../../../../../store/src/actions/dashboard-actions'; import { RouterNav } from '../../../../../store/src/actions/router.actions'; -import { AppState, IRequestEntityTypeState } from '../../../../../store/src/app-state'; +import { AppState } from '../../../../../store/src/app-state'; import { EndpointModel, entityCatalog } from '../../../../../store/src/public-api'; import { selectDashboardState } from '../../../../../store/src/selectors/dashboard.selectors'; import { UserFavoriteManager } from '../../../../../store/src/user-favorite-manager'; @@ -95,12 +95,11 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { this.layouts$ = of(this.layouts); this.layout$ = this.layout.asObservable(); - this.allEndpointIds$ = this.endpointsService.endpoints$.pipe( + this.allEndpointIds$ = this.endpointsService.connectedEndpoints$.pipe( map(endpoints => Object.values(endpoints).map(endpoint => endpoint.guid)) ); this.haveRegistered$ = this.endpointsService.haveRegistered$; - - const connected$ = this.endpointsService.endpoints$; + const connected$ = this.endpointsService.connectedEndpoints$; // Only show endpoints that have Home Card metadata this.endpoints$ = combineLatest([connected$, userFavoriteManager.getAllFavorites()]).pipe( @@ -235,7 +234,7 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { // 1. Endpoint has been added as a favourite // 2. Endpoint that has child favourites // 3. Remaining endpoints - private orderEndpoints(endpoints: IRequestEntityTypeState, favorites: IUserFavoritesGroups): EndpointModel[] { + private orderEndpoints(endpoints: EndpointModel[], favorites: IUserFavoritesGroups): EndpointModel[] { const processed = {}; const result = []; @@ -259,7 +258,7 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { } }); - Object.values(endpoints).forEach(ep => { + endpoints.forEach(ep => { if (!processed[ep.guid]) { processed[ep.guid] = true; result.push(ep); @@ -272,8 +271,7 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { // Automatic layout - select the best layout based on the available endpoints private automaticLayout(): Observable { - return this.endpointsService.endpoints$.pipe( - map(eps => Object.values(eps)), + return this.endpointsService.connectedEndpoints$.pipe( map(eps => eps.filter(ep => { const defn = entityCatalog.getEndpoint(ep.cnsi_type, ep.sub_type); return !!defn.definition.homeCard; From 834ad4f0d34c752b44f6ac5d5f7690e67e63eae8 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 13 Nov 2020 19:48:55 +0000 Subject: [PATCH 49/74] Address PR feedback --- .../packages/cloud-foundry/src/cf-entity-generator.ts | 5 ----- .../card-cf-recent-apps/card-cf-recent-apps.component.scss | 3 +-- .../core/src/features/home/home/home-page.component.html | 3 ++- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index 41bb31d24e..b0038f427c 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -245,11 +245,6 @@ function cfShortcuts(id: string) { icon: 'organization', iconFont: 'stratos-icons' }, - { - title: 'View Applications', - link: ['/cloud-foundry', id, 'organizations'], - icon: 'apps' - }, { title: 'Deploy an Application', link: ['/applications', 'new', id], diff --git a/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.scss b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.scss index e376d37732..96249b7129 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.scss +++ b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.scss @@ -37,11 +37,10 @@ overflow-y: auto; width: 100%; } - } // Remove box shadow in plain mode -.recent-apps-card.mat-card.recent-apps-card__plain { +mat-card.recent-apps-card.mat-card.recent-apps-card__plain { box-shadow: none; padding: 0 1em; } diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.html b/src/frontend/packages/core/src/features/home/home/home-page.component.html index c3dada5478..d534f44f93 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.html +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.html @@ -27,7 +27,8 @@

Home

- +
From 17f67fd4c880934a8012e3f45490e4959400c730 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 13 Nov 2020 19:58:58 +0000 Subject: [PATCH 50/74] Restore apps link --- .../cloud-foundry/src/cf-entity-generator.ts | 5 +++++ .../application-wall.component.spec.ts | 9 +++++++++ .../application-wall/application-wall.component.ts | 14 +++++++++++++- .../features/applications/applications.routing.ts | 5 +++++ 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index b0038f427c..56e70242de 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -245,6 +245,11 @@ function cfShortcuts(id: string) { icon: 'organization', iconFont: 'stratos-icons' }, + { + title: 'View Applications', + link: ['/applications', id], + icon: 'apps' + }, { title: 'Deploy an Application', link: ['/applications', 'new', id], diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.spec.ts b/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.spec.ts index e7060da064..95bd0263e7 100644 --- a/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.spec.ts +++ b/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.spec.ts @@ -1,5 +1,6 @@ import { DatePipe } from '@angular/common'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; import { TabNavService } from '../../../../../core/src/tab-nav.service'; import { generateCfBaseTestModules } from '../../../../test-framework/cloud-foundry-endpoint-service.helper'; @@ -24,6 +25,14 @@ describe('ApplicationWallComponent', () => { DatePipe, TabNavService, CloudFoundryService, + { + provide: ActivatedRoute, + useValue: { + snapshot: { + params: {}, + } + } + } ] }) .compileComponents(); diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.ts b/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.ts index c6cfa5d7dc..79bb6cb74c 100644 --- a/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.ts @@ -1,5 +1,6 @@ import { animate, query, style, transition, trigger } from '@angular/animations'; import { Component, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; import { Store } from '@ngrx/store'; import { Observable, Subscription } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -12,6 +13,7 @@ import { CfAppsDataSource } from '../../../shared/components/list/list-types/app import { CfOrgSpaceDataService, initCfOrgSpaceService } from '../../../shared/data-services/cf-org-space-service.service'; import { CloudFoundryService } from '../../../shared/data-services/cloud-foundry.service'; import { CfCurrentUserPermissions } from '../../../user-permissions/cf-user-permissions-checkers'; +import { goToAppWall } from '../../cf/cf.helpers'; @Component({ selector: 'app-application-wall', @@ -48,7 +50,15 @@ export class ApplicationWallComponent implements OnDestroy { public cloudFoundryService: CloudFoundryService, private store: Store, private cfOrgSpaceService: CfOrgSpaceDataService, + activatedRoute: ActivatedRoute, ) { + // If we have an endpoint ID, select it and redirect + const { endpointId } = activatedRoute.snapshot.params; + if (endpointId) { + goToAppWall(this.store, endpointId); + return; + } + this.cfIds$ = cloudFoundryService.cFEndpoints$.pipe( map(endpoints => endpoints.map(endpoint => endpoint.guid)), ); @@ -65,6 +75,8 @@ export class ApplicationWallComponent implements OnDestroy { } ngOnDestroy(): void { - this.initCfOrgSpaceService.unsubscribe(); + if (this.initCfOrgSpaceService) { + this.initCfOrgSpaceService.unsubscribe(); + } } } diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/applications.routing.ts b/src/frontend/packages/cloud-foundry/src/features/applications/applications.routing.ts index 656da9872d..d8f3bf62cf 100644 --- a/src/frontend/packages/cloud-foundry/src/features/applications/applications.routing.ts +++ b/src/frontend/packages/cloud-foundry/src/features/applications/applications.routing.ts @@ -65,6 +65,11 @@ const applicationsRoutes: Routes = [ extensionsActionsKey: StratosActionType.Applications } }, + { + path: ':endpointId', + component: ApplicationWallComponent, + pathMatch: 'full' + }, { path: ':endpointId/:id', component: ApplicationBaseComponent, From b9e1c88c9fe5f458ea8df4737d82cf1f91847e06 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 13 Nov 2020 20:44:31 +0000 Subject: [PATCH 51/74] Improve typing --- .../packages/core/src/features/home/home.types.ts | 5 ++--- .../core/src/features/home/home/home-page.component.ts | 4 ++-- .../store/src/entity-catalog/entity-catalog.types.ts | 2 ++ .../user-favorites-groups.reducer.ts | 3 +++ .../packages/store/src/types/favorite-groups.types.ts | 8 +++----- src/frontend/packages/store/src/user-favorite-manager.ts | 6 ------ 6 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home.types.ts b/src/frontend/packages/core/src/features/home/home.types.ts index 2e60d1afc8..fc0f68c1fc 100644 --- a/src/frontend/packages/core/src/features/home/home.types.ts +++ b/src/frontend/packages/core/src/features/home/home.types.ts @@ -2,7 +2,7 @@ import { Observable } from 'rxjs'; import { HomeCardShortcut } from '../../../../store/src/entity-catalog/entity-catalog.types'; import { EndpointModel } from '../../../../store/src/public-api'; -import { IHydrationResults } from '../../../../store/src/types/user-favorite-manager.types'; +import { UserFavorite } from './../../../../store/src/types/user-favorites.types'; // Layout for a home page card @@ -23,7 +23,6 @@ export abstract class HomePageEndpointCard { } export interface LinkMetadata { - favs: IHydrationResults[], + favs: UserFavorite[], shortcuts: HomeCardShortcut[] } - diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.ts index 8b44305217..f80b8119a8 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.ts @@ -240,7 +240,7 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { Object.keys(favorites).forEach(fav => { if (!favorites[fav].ethereal) { - const id = this.userFavoriteManager.getEndpointIDFromFavoriteID(fav); + const id = favorites[fav].endpoint.endpointId; if (!!endpoints[id] && !processed[id]) { processed[id] = true; result.push(endpoints[id]); @@ -250,7 +250,7 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { Object.keys(favorites).forEach(fav => { if (favorites[fav].ethereal) { - const id = this.userFavoriteManager.getEndpointIDFromFavoriteID(fav); + const id = favorites[fav].endpoint.endpointId; if (!!endpoints[id] && !processed[id]) { processed[id] = true; result.push(endpoints[id]); diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts index bd17292384..9376fc7a8a 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts @@ -237,6 +237,8 @@ export interface IStratosEntityBuilder { singular: string, plural: string, }; + // Is the underlying entity for the favorite valid? + getIsValid?(entityMetadata): Observable; /** * Actions that don't effect an individual entity i.e. create new * @returns global actions diff --git a/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts b/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts index 463b41c655..bd8b370e77 100644 --- a/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts +++ b/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts @@ -131,6 +131,9 @@ function addFavoriteToGroup(favoriteGroup: IUserFavoriteGroup = getDefaultFavori }; const { guid } = favorite; const isEndpoint = isEndpointTypeFavorite(favorite); + if (isEndpoint) { + fg.endpoint = favorite; + } if (!isEndpoint && guid && !fg.entitiesIds.includes(guid)) { fg.entitiesIds.push(guid); } diff --git a/src/frontend/packages/store/src/types/favorite-groups.types.ts b/src/frontend/packages/store/src/types/favorite-groups.types.ts index b9c445357b..daeafc5f8c 100644 --- a/src/frontend/packages/store/src/types/favorite-groups.types.ts +++ b/src/frontend/packages/store/src/types/favorite-groups.types.ts @@ -1,4 +1,5 @@ import { ActionState } from '../reducers/api-request-reducer/types'; +import { IFavoriteMetadata, UserFavorite } from './user-favorites.types'; export interface IUserFavoritesGroupsState extends ActionState { groups: IUserFavoritesGroups; @@ -7,19 +8,16 @@ export interface IUserFavoritesGroups { [endpointGuid: string]: IUserFavoriteGroup; } - export interface IUserFavoriteGroup { + endpoint: UserFavorite; // Did we automatically add the endpoint to the group? ethereal: boolean; - search: string; - typeFilter: string; entitiesIds: string[]; } export const getDefaultFavoriteGroup = (): IUserFavoriteGroup => ({ + endpoint: {} as UserFavorite, ethereal: true, - search: null, - typeFilter: null, entitiesIds: [] }); diff --git a/src/frontend/packages/store/src/user-favorite-manager.ts b/src/frontend/packages/store/src/user-favorite-manager.ts index 76c483b2d5..f680a05dee 100644 --- a/src/frontend/packages/store/src/user-favorite-manager.ts +++ b/src/frontend/packages/store/src/user-favorite-manager.ts @@ -144,12 +144,6 @@ export class UserFavoriteManager { ) } - public getEndpointIDFromFavoriteID(id: string): string { - const p = id.split('-'); - const idParts = p.slice(0, p.length - 2); - return idParts.join('-'); - } - /** * For a given favorite, return the corresponding metadata */ From 93e1c9ea36f8e01e1c67e9728af9bfd1f724b960 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 13 Nov 2020 20:49:23 +0000 Subject: [PATCH 52/74] Fix undefined error --- .../autoscaler-tab-extension.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.ts b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.ts index a71a1e76e1..9580b081ba 100644 --- a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.ts +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.ts @@ -77,8 +77,8 @@ import { appAutoscalerAppMetricEntityType, autoscalerEntityFactory } from '../.. switchMap(app => cups.can( CfCurrentUserPermissions.APPLICATION_EDIT, endpointGuid, - app.entity.entity.space.entity.organization_guid, - app.entity.entity.space.metadata.guid + app.entity.entity.space?.entity.organization_guid, + app.entity.entity.space?.metadata.guid )), ); From d65ed30957912b9ef480f5095bb09543b8ecf09d Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 13 Nov 2020 22:16:54 +0000 Subject: [PATCH 53/74] Fix unit test --- .../application-wall/application-wall.component.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.spec.ts b/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.spec.ts index 95bd0263e7..3c7ef0d531 100644 --- a/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.spec.ts +++ b/src/frontend/packages/cloud-foundry/src/features/applications/application-wall/application-wall.component.spec.ts @@ -30,6 +30,7 @@ describe('ApplicationWallComponent', () => { useValue: { snapshot: { params: {}, + queryParams: {} } } } From a320652a76a82637e224f71864e346741ac05561 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Sat, 14 Nov 2020 12:14:09 +0000 Subject: [PATCH 54/74] Fix ordering issue --- .../features/home/home/home-page.component.ts | 19 +++++++++---------- .../user-favorites-groups.reducer.ts | 5 +++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.ts index f80b8119a8..b7d24d8d02 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.ts @@ -1,11 +1,9 @@ import { ScrollDispatcher } from '@angular/cdk/scrolling'; -import { DOCUMENT } from '@angular/common'; import { AfterViewInit, Component, ElementRef, HostListener, - Inject, OnDestroy, OnInit, QueryList, @@ -76,7 +74,6 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { private store: Store, public userFavoriteManager: UserFavoriteManager, private scrollDispatcher: ScrollDispatcher, - @Inject(DOCUMENT) private document ) { // Redirect to /applications if not enabled endpointsService.disablePersistenceFeatures$.pipe( @@ -107,7 +104,8 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { const ordered = this.orderEndpoints(endpoints, favGroups); return ordered.filter(ep => { const defn = entityCatalog.getEndpoint(ep.cnsi_type, ep.sub_type); - return !!defn.definition.homeCard; + const connected = defn.definition.unConnectable || ep.connectionStatus === 'connected'; + return connected; }); }) ); @@ -237,13 +235,15 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { private orderEndpoints(endpoints: EndpointModel[], favorites: IUserFavoritesGroups): EndpointModel[] { const processed = {}; const result = []; + const epMap = {}; + endpoints.forEach(ep => epMap[ep.guid] = ep); Object.keys(favorites).forEach(fav => { if (!favorites[fav].ethereal) { const id = favorites[fav].endpoint.endpointId; - if (!!endpoints[id] && !processed[id]) { + if (!!epMap[id] && !processed[id]) { processed[id] = true; - result.push(endpoints[id]); + result.push(epMap[id]); } } }); @@ -251,9 +251,9 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { Object.keys(favorites).forEach(fav => { if (favorites[fav].ethereal) { const id = favorites[fav].endpoint.endpointId; - if (!!endpoints[id] && !processed[id]) { + if (!!epMap[id] && !processed[id]) { processed[id] = true; - result.push(endpoints[id]); + result.push(epMap[id]); } } }); @@ -265,8 +265,7 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { } }) - // Filter out the disconnected ones - return result.filter(ep => ep.connectionStatus === 'connected'); + return result; } // Automatic layout - select the best layout based on the available endpoints diff --git a/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts b/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts index bd8b370e77..e22b385c39 100644 --- a/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts +++ b/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts @@ -61,8 +61,8 @@ export function userFavoriteGroupsReducer( function buildFavoritesGroups(action: GetUserFavoritesSuccessAction) { const { favorites } = action; return favorites.reduce((favoriteGroups, favorite) => { - const { guid } = deriveEndpointFavoriteFromFavorite(favorite); - favoriteGroups[guid] = addFavoriteToGroup(favoriteGroups[guid], favorite); + const userFavorute = deriveEndpointFavoriteFromFavorite(favorite); + favoriteGroups[userFavorute.guid] = addFavoriteToGroup(favoriteGroups[userFavorute.guid], userFavorute); return favoriteGroups; }, {} as IUserFavoritesGroups); } @@ -129,6 +129,7 @@ function addFavoriteToGroup(favoriteGroup: IUserFavoriteGroup = getDefaultFavori ...favoriteGroup.entitiesIds ] }; + const { guid } = favorite; const isEndpoint = isEndpointTypeFavorite(favorite); if (isEndpoint) { From e4334ae51613e94435598bbd43ff376e0664a694 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 16 Nov 2020 10:35:09 +0000 Subject: [PATCH 55/74] Add a default home card --- .../core/src/features/home/home.module.ts | 4 ++ ...ult-endpoint-home-component.component.html | 7 ++++ ...ult-endpoint-home-component.component.scss | 14 +++++++ ...-endpoint-home-component.component.spec.ts | 25 ++++++++++++ ...fault-endpoint-home-component.component.ts | 38 +++++++++++++++++++ .../home-page-endpoint-card.component.ts | 26 ++++++++++++- .../user-favorites-groups.reducer.ts | 4 +- .../store/src/user-favorite-manager.ts | 16 ++++++++ 8 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.html create mode 100644 src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.scss create mode 100644 src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.spec.ts create mode 100644 src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.ts diff --git a/src/frontend/packages/core/src/features/home/home.module.ts b/src/frontend/packages/core/src/features/home/home.module.ts index 702a3ae973..30d9845f91 100644 --- a/src/frontend/packages/core/src/features/home/home.module.ts +++ b/src/frontend/packages/core/src/features/home/home.module.ts @@ -3,6 +3,9 @@ import { RouterModule } from '@angular/router'; import { CoreModule, SharedModule } from '@stratosui/core'; import { MDAppModule } from './../../core/md.module'; +import { + DefaultEndpointHomeComponent, +} from './home/default-endpoint-home-component/default-endpoint-home-component.component'; import { FavoritesMetaCardComponent } from './home/favorites-meta-card/favorites-meta-card.component'; import { FavoritesSidePanelComponent } from './home/favorites-side-panel/favorites-side-panel.component'; import { HomePageEndpointCardComponent } from './home/home-page-endpoint-card/home-page-endpoint-card.component'; @@ -23,6 +26,7 @@ import { HomeShortcutsComponent } from './home/home-shortcuts/home-shortcuts.com FavoritesMetaCardComponent, HomeShortcutsComponent, FavoritesSidePanelComponent, + DefaultEndpointHomeComponent, ], exports: [ FavoritesMetaCardComponent, diff --git a/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.html b/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.html new file mode 100644 index 0000000000..a812a38cc6 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.html @@ -0,0 +1,7 @@ +
+ +
+
{{ url }}
+
+
+
\ No newline at end of file diff --git a/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.scss b/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.scss new file mode 100644 index 0000000000..e6d1a5abe5 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.scss @@ -0,0 +1,14 @@ +.default-home-card { + margin: 1em; + + &__address { + display: flex; + min-height: 24px; + &__value { + align-items: center; + display: flex; + margin-right: 28px; + word-break: break-all; + } + } +} \ No newline at end of file diff --git a/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.spec.ts b/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.spec.ts new file mode 100644 index 0000000000..347193314a --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DefaultEndpointHomeComponentComponent } from './default-endpoint-home-component.component'; + +describe('DefaultEndpointHomeComponentComponent', () => { + let component: DefaultEndpointHomeComponentComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DefaultEndpointHomeComponentComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DefaultEndpointHomeComponentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.ts b/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.ts new file mode 100644 index 0000000000..21dd9a9d13 --- /dev/null +++ b/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.ts @@ -0,0 +1,38 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { Observable, of } from 'rxjs'; + +import { getFullEndpointApiUrl } from '../../../../../../store/src/endpoint-utils'; +import { EndpointModel } from '../../../../../../store/src/public-api'; +import { HomePageCardLayout, HomePageEndpointCard } from '../../home.types'; + +@Component({ + selector: 'app-default-endpoint-home-component', + templateUrl: './default-endpoint-home-component.component.html', + styleUrls: ['./default-endpoint-home-component.component.scss'] +}) +export class DefaultEndpointHomeComponent implements OnInit, HomePageEndpointCard { + + url: string; + + _layout: HomePageCardLayout; + + get layout(): HomePageCardLayout { + return this._layout; + } + + @Input() set layout(value: HomePageCardLayout) { + if (value) { + this._layout = value; + } + }; + + @Input() endpoint: EndpointModel; + + ngOnInit(): void { + this.url = getFullEndpointApiUrl(this.endpoint); + } + + load(): Observable { + return of(true); + } +} diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index d268c93c5e..6ba4e1661d 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -2,6 +2,7 @@ import { AfterViewInit, Compiler, Component, + ComponentFactoryResolver, ComponentRef, EventEmitter, Injector, @@ -25,6 +26,9 @@ import { SidePanelMode, SidePanelService } from '../../../../shared/services/sid import { FavoritesSidePanelComponent } from '../favorites-side-panel/favorites-side-panel.component'; import { UserFavoriteEndpoint } from './../../../../../../store/src/types/user-favorites.types'; import { HomePageCardLayout, HomePageEndpointCard, LinkMetadata } from './../../home.types'; +import { + DefaultEndpointHomeComponent, +} from './../default-endpoint-home-component/default-endpoint-home-component.component'; const MAX_FAVS_NORMAL = 15; const MAX_FAVS_COMPACT = 5; @@ -95,11 +99,16 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi // Should the Home Card use the whole width, or do we show the links panel as well? fullView = false; + // Does the endpoint haev entities that can be favourited + // If not, then don't show favorites, as there can never be any + hasFavEntities = false; + constructor( private userFavoriteManager: UserFavoriteManager, private sidePanelService: SidePanelService, private compiler: Compiler, private injector: Injector, + private componentFactoryResolver: ComponentFactoryResolver, ) { this.status$ = this.status.asObservable(); } @@ -111,10 +120,12 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi this.createCard(endpointEntity); } else { console.warn(`No endpoint home card for ${this.endpoint.guid}`); + this.createCard(undefined); } } ngOnInit() { + this.hasFavEntities = this.userFavoriteManager.endpointHasEntitiesThatCanFavorite(this.endpoint.cnsi_type); // Favorites for this endpoint this.favorites$ = this.userFavoriteManager.getFavoritesForEndpoint(this.endpoint.guid); @@ -161,6 +172,11 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi this.showShortcutsOnSide = false; } } + + // If nothing can be favorited and there are no shotrcuts then hide the right-hand side panel + if (!this.hasFavEntities && shortcuts.length === 0) { + setTimeout(() => this.fullView = true, 0); + } return { favs, shortcuts @@ -188,7 +204,14 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi async createCard(endpointEntity: any) { this.customCard.clear(); - const component = await endpointEntity.definition.homeCard.component(this.compiler, this.injector); + + let component; + if (!endpointEntity) { + component = this.componentFactoryResolver.resolveComponentFactory(DefaultEndpointHomeComponent); + } else { + component = await endpointEntity.definition.homeCard.component(this.compiler, this.injector); + } + this.ref = this.customCard.createComponent(component); this.ref.instance.endpoint = this.endpoint; this.ref.instance.layout = this._layout; @@ -211,7 +234,6 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi this.sub = loadObs.pipe(timeout(15000), filter(v => v === true), first()).subscribe(() => { this.loaded.next(); setTimeout(() => this.status.next(Status.OK), 0); - this.sub.unsubscribe(); }, () => { this.loaded.next(); this.status.next(Status.Error); diff --git a/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts b/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts index e22b385c39..3b99042c6d 100644 --- a/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts +++ b/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts @@ -61,8 +61,8 @@ export function userFavoriteGroupsReducer( function buildFavoritesGroups(action: GetUserFavoritesSuccessAction) { const { favorites } = action; return favorites.reduce((favoriteGroups, favorite) => { - const userFavorute = deriveEndpointFavoriteFromFavorite(favorite); - favoriteGroups[userFavorute.guid] = addFavoriteToGroup(favoriteGroups[userFavorute.guid], userFavorute); + const userFavorite = deriveEndpointFavoriteFromFavorite(favorite); + favoriteGroups[userFavorite.guid] = addFavoriteToGroup(favoriteGroups[userFavorite.guid], favorite); return favoriteGroups; }, {} as IUserFavoritesGroups); } diff --git a/src/frontend/packages/store/src/user-favorite-manager.ts b/src/frontend/packages/store/src/user-favorite-manager.ts index f680a05dee..8d721201fb 100644 --- a/src/frontend/packages/store/src/user-favorite-manager.ts +++ b/src/frontend/packages/store/src/user-favorite-manager.ts @@ -196,4 +196,20 @@ export class UserFavoriteManager { ); } + // Determine is an endpoint has any entities that can be favorited + public endpointHasEntitiesThatCanFavorite(endpointType: string) { + const entities = entityCatalog.getAllEntitiesForEndpointType(endpointType); + let total = 0; + entities.forEach(e => { + const defn = e.builders?.entityBuilder; + if (defn) { + const canFavorite = defn.getGuid && defn.getMetadata && defn.getLink; + if (canFavorite) { + total++; + } + } + }); + return total > 0; + } + } From 86dd50037de3b733bba74734533b7d59dfbac4be Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 16 Nov 2020 13:32:20 +0000 Subject: [PATCH 56/74] Fix frong end lint issues --- .../card-cf-recent-apps.component.ts | 4 ++-- .../compact-app-card.component.ts | 4 ++-- .../home/cfhome-card/cfhome-card.component.ts | 17 ++++++++--------- .../core/src/features/home/home.types.ts | 5 ++--- ...fault-endpoint-home-component.component.ts | 8 ++++---- .../favorites-meta-card.component.ts | 4 ++-- .../favorites-side-panel.component.ts | 2 +- .../home-page-endpoint-card.component.spec.ts | 2 +- .../home-page-endpoint-card.component.ts | 19 +++++++++---------- .../features/home/home/home-page.component.ts | 12 ++++++------ .../home/kubernetes-home-card.component.ts | 8 ++++---- src/frontend/packages/store/src/operators.ts | 4 ++-- src/frontend/packages/store/src/public-api.ts | 2 +- .../store/src/types/user-favorites.types.ts | 2 +- .../store/src/user-favorite-manager.ts | 4 ++-- 15 files changed, 47 insertions(+), 50 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.ts index bd5bfa11db..69179fc0f4 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/card-cf-recent-apps.component.ts @@ -61,7 +61,7 @@ export class CardCfRecentAppsComponent implements OnInit { if (!this.allApps$) { this.allApps$ = this.appsPagObs.entities$; this.loading$ = this.appsPagObs.fetchingEntities$; - this.hasEntities$ = this.appsPagObs.hasEntities$ + this.hasEntities$ = this.appsPagObs.hasEntities$; } else { this.hasEntities$ = of(true); } @@ -84,7 +84,7 @@ export class CardCfRecentAppsComponent implements OnInit { } private fetchAppStats(recentApps: APIResource[]) { - if(!this.noStats) { + if (!this.noStats) { recentApps.forEach(app => { if (app.entity.state === 'STARTED') { cfEntityCatalog.appStats.api.getMultiple(app.metadata.guid, this.endpoint); diff --git a/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts index 6e3aef7245..c78c819bd1 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/card-cf-recent-apps/compact-app-card/compact-app-card.component.ts @@ -39,7 +39,7 @@ export class CompactAppCardComponent implements OnInit { ) { } ngOnInit() { - if(this.activeRouteCfOrgSpace) { + if (this.activeRouteCfOrgSpace) { this.bcType = this.setBreadcrumbType(this.activeRouteCfOrgSpace); if (!this.endpoint) { this.endpoint = this.activeRouteCfOrgSpace.cfGuid; @@ -47,7 +47,7 @@ export class CompactAppCardComponent implements OnInit { } if (!this.app) { - return + return; } const initState = this.appStateService.get(this.app.entity, null); diff --git a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts index 4c522f6129..6ea7e7df90 100644 --- a/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/home/cfhome-card/cfhome-card.component.ts @@ -42,18 +42,18 @@ import { ITileConfig } from './../../../../../core/src/shared/components/tile/ti }) export class CFHomeCardComponent implements HomePageEndpointCard { - _layout: HomePageCardLayout; + pLayout: HomePageCardLayout; get layout(): HomePageCardLayout { - return this._layout; + return this.pLayout; } @Input() set layout(value: HomePageCardLayout) { if (value) { - this._layout = value; + this.pLayout = value; } this.updateLayout(); - }; + } @Input() set endpoint(value: EndpointModel) { this.guid = value.guid; @@ -89,7 +89,7 @@ export class CFHomeCardComponent implements HomePageEndpointCard { appDeploySourceTypes: ApplicationDeploySourceTypes, ) { // Set a default layout - this._layout = new HomePageCardLayout(1, 1); + this.pLayout = new HomePageCardLayout(1, 1); // Get source types for if we are showing tiles to deploy an application this.sourceTypes = appDeploySourceTypes.getTypes(); @@ -110,7 +110,7 @@ export class CFHomeCardComponent implements HomePageEndpointCard { if (tile) { const query = { [BASE_REDIRECT_QUERY]: `applications/new/${this.guid}`, - [AUTO_SELECT_CF_URL_PARAM]:this.guid + [AUTO_SELECT_CF_URL_PARAM]: this.guid }; if (tile.data.subType) { query[AUTO_SELECT_DEPLOY_TYPE_URL_PARAM] = tile.data.subType; @@ -126,7 +126,7 @@ export class CFHomeCardComponent implements HomePageEndpointCard { this.appCount$ = CloudFoundryEndpointService.fetchAppCount(this.store, this.pmf, this.guid); this.orgCount$ = CloudFoundryEndpointService.fetchOrgCount(this.store, this.pmf, this.guid); - this.appLink = () => goToAppWall(this.store, this.guid);; + this.appLink = () => goToAppWall(this.store, this.guid); const appsPagObs = cfEntityCatalog.application.store.getPaginationService(this.guid); @@ -192,8 +192,7 @@ export class CFHomeCardComponent implements HomePageEndpointCard { } private restrictApps(apps: APIResource[]): APIResource[] { - return !apps ? [] :[...apps.sort(appDataSort).slice(0, this.recentAppsRows)]; + return !apps ? [] : [...apps.sort(appDataSort).slice(0, this.recentAppsRows)]; } } - diff --git a/src/frontend/packages/core/src/features/home/home.types.ts b/src/frontend/packages/core/src/features/home/home.types.ts index 2e60d1afc8..aeae4b9892 100644 --- a/src/frontend/packages/core/src/features/home/home.types.ts +++ b/src/frontend/packages/core/src/features/home/home.types.ts @@ -23,7 +23,6 @@ export abstract class HomePageEndpointCard { } export interface LinkMetadata { - favs: IHydrationResults[], - shortcuts: HomeCardShortcut[] + favs: IHydrationResults[]; + shortcuts: HomeCardShortcut[]; } - diff --git a/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.ts b/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.ts index 21dd9a9d13..12733ebc17 100644 --- a/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.ts +++ b/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.ts @@ -14,17 +14,17 @@ export class DefaultEndpointHomeComponent implements OnInit, HomePageEndpointCar url: string; - _layout: HomePageCardLayout; + pLayout: HomePageCardLayout; get layout(): HomePageCardLayout { - return this._layout; + return this.pLayout; } @Input() set layout(value: HomePageCardLayout) { if (value) { - this._layout = value; + this.pLayout = value; } - }; + } @Input() endpoint: EndpointModel; diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts index c5adf81166..547b59827c 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts @@ -45,7 +45,7 @@ export class FavoritesMetaCardComponent { this.favorite = favoriteEntity; this.favoriteType = this.favorite.getPrettyTypeName(); this.icon = this.favorite.getIcon(); - this.name = this.favorite.metadata.name + this.name = this.favorite.metadata.name; this.routerLink = this.favorite.getLink(); } } @@ -66,7 +66,7 @@ export class FavoritesMetaCardComponent { 'Remove', true ); - this.confirmDialog.open(confirmation, () => { this.userFavoriteManager.toggleFavorite(this.favorite); }) + this.confirmDialog.open(confirmation, () => { this.userFavoriteManager.toggleFavorite(this.favorite); }); } else { // Navigate to the favorite this.router.navigate([this.routerLink]); diff --git a/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.ts b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.ts index cc886ef1dd..76dde68721 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.ts +++ b/src/frontend/packages/core/src/features/home/home/favorites-side-panel/favorites-side-panel.component.ts @@ -14,7 +14,7 @@ export class FavoritesSidePanelComponent implements PreviewableComponent { name: string; setProps(props: { [key: string]: any; }): void { - this.favorites$ = props.favorites$ + this.favorites$ = props.favorites$; this.name = props.endpoint.name; } diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts index eb75b1541c..3a027f1a0a 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.spec.ts @@ -42,7 +42,7 @@ describe('HomePageEndpointCardComponent', () => { afterEach(() => { component.ngOnDestroy(); - }) + }); it('should create', () => { expect(component).toBeTruthy(); diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index 6ba4e1661d..3bcdcc45bc 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -50,22 +50,22 @@ enum Status { }) export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterViewInit { - @ViewChild('customCard', {read:ViewContainerRef}) customCard: ViewContainerRef; + @ViewChild('customCard', {read: ViewContainerRef}) customCard: ViewContainerRef; @Input() endpoint: EndpointModel; - _layout: HomePageCardLayout; + pLayout: HomePageCardLayout; get layout(): HomePageCardLayout { - return this._layout; + return this.pLayout; } @Input() set layout(value: HomePageCardLayout) { if (value) { - this._layout = value; + this.pLayout = value; } this.updateLayout(); - }; + } @Output() loaded = new EventEmitter(); @@ -115,7 +115,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi ngAfterViewInit() { // Dynamically load the component for the Home Card for this endopoint - const endpointEntity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type) + const endpointEntity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type); if (endpointEntity && endpointEntity.definition.homeCard && endpointEntity.definition.homeCard.component) { this.createCard(endpointEntity); } else { @@ -128,8 +128,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi this.hasFavEntities = this.userFavoriteManager.endpointHasEntitiesThatCanFavorite(this.endpoint.cnsi_type); // Favorites for this endpoint this.favorites$ = this.userFavoriteManager.getFavoritesForEndpoint(this.endpoint.guid); - - this.entity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type) + this.entity = entityCatalog.getEndpoint(this.endpoint.cnsi_type, this.endpoint.sub_type); if (this.entity) { this.definition = this.entity.definition; this.favorite = this.userFavoriteManager.getFavoriteEndpointFromEntity(this.endpoint); @@ -198,7 +197,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi public updateLayout() { this.layout$.next(this.layout); if (this.ref && this.ref.instance) { - this.ref.instance.layout = this._layout; + this.ref.instance.layout = this.pLayout; } } @@ -214,7 +213,7 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi this.ref = this.customCard.createComponent(component); this.ref.instance.endpoint = this.endpoint; - this.ref.instance.layout = this._layout; + this.ref.instance.layout = this.pLayout; this.loadCardIfReady(); } diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.ts b/src/frontend/packages/core/src/features/home/home/home-page.component.ts index b7d24d8d02..1148aa2675 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.ts @@ -121,7 +121,7 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { ).subscribe(id => { const selected = this.layouts.find(hpcl => hpcl && hpcl.id === id) || this.layouts[0]; this.onChangeLayout(selected); - }) + }); } ngOnInit() { @@ -150,10 +150,10 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { } else { remaining.push(index); } - }; + } this.processCardsToLoad(); this.notLoadedCardIndices = remaining; - }) + }); } processCardsToLoad() { @@ -182,7 +182,7 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { setCardsToLoad(cards: any[]) { this.notLoadedCardIndices = []; - for(let i=0;i< cards.length; i++) { + for (let i = 0; i < cards.length; i++) { this.notLoadedCardIndices.push(i); } setTimeout(() => this.checkCardsInView(), 1); @@ -263,7 +263,7 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { processed[ep.guid] = true; result.push(ep); } - }) + }); return result; } @@ -276,7 +276,7 @@ export class HomePageComponent implements AfterViewInit, OnInit, OnDestroy { return !!defn.definition.homeCard; })), map(eps => { - switch(eps.length) { + switch (eps.length) { case 1: return this.getLayout(1, 1); case 2: diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts index 1d4926219c..2279a142cb 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts @@ -17,17 +17,17 @@ export class KubernetesHomeCardComponent implements OnInit { @Input() endpoint: EndpointModel; - _layout: HomePageCardLayout; + pLayout: HomePageCardLayout; get layout(): HomePageCardLayout { - return this._layout; + return this.pLayout; } @Input() set layout(value: HomePageCardLayout) { if (value) { - this._layout = value; + this.pLayout = value; } - }; + } public shortcuts: HomeCardShortcut[]; diff --git a/src/frontend/packages/store/src/operators.ts b/src/frontend/packages/store/src/operators.ts index e490ab161e..21ccb2a7a2 100644 --- a/src/frontend/packages/store/src/operators.ts +++ b/src/frontend/packages/store/src/operators.ts @@ -11,5 +11,5 @@ export function entityFetchedWithoutError(): OperatorFunction { filter(([oldV, newV]) => (oldV as any).fetching && !(newV as any).fetching), map(([, newV]) => newV), map(f => !(f as any).error) - ) -}; + ); +} diff --git a/src/frontend/packages/store/src/public-api.ts b/src/frontend/packages/store/src/public-api.ts index f355a7ee1a..e85473177c 100644 --- a/src/frontend/packages/store/src/public-api.ts +++ b/src/frontend/packages/store/src/public-api.ts @@ -26,4 +26,4 @@ export { WrapperRequestActionSuccess } from './types/request.types'; export { flattenPagination, PaginationFlattener } from './helpers/paginated-request-helpers'; // Operators -export { entityFetchedWithoutError } from './operators'; \ No newline at end of file +export { entityFetchedWithoutError } from './operators'; diff --git a/src/frontend/packages/store/src/types/user-favorites.types.ts b/src/frontend/packages/store/src/types/user-favorites.types.ts index 29a9658d1c..07ece3e6f2 100644 --- a/src/frontend/packages/store/src/types/user-favorites.types.ts +++ b/src/frontend/packages/store/src/types/user-favorites.types.ts @@ -109,7 +109,7 @@ export class UserFavorite implement return { icon: defn.icon || 'help', iconFont: defn.iconFont - } + }; } private buildFavoriteStoreEntityGuid() { diff --git a/src/frontend/packages/store/src/user-favorite-manager.ts b/src/frontend/packages/store/src/user-favorite-manager.ts index 8d721201fb..6609e6b9c1 100644 --- a/src/frontend/packages/store/src/user-favorite-manager.ts +++ b/src/frontend/packages/store/src/user-favorite-manager.ts @@ -138,10 +138,10 @@ export class UserFavoriteManager { // Ensure we actually have a UserFavorite object and not a struct result.push(this.getUserFavoriteFromObject(f)); } - }) + }); return result; }) - ) + ); } /** From fbecdf569ff9416e647908b76b3a9c5d88f5b7df Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 16 Nov 2020 13:44:11 +0000 Subject: [PATCH 57/74] Merge fixes --- src/frontend/packages/core/src/features/home/home.types.ts | 4 ++-- .../favorites-meta-card/favorites-meta-card.component.html | 6 +++--- .../src/kubernetes/kubernetes-entity-generator.ts | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home.types.ts b/src/frontend/packages/core/src/features/home/home.types.ts index aeae4b9892..31a1d43b9b 100644 --- a/src/frontend/packages/core/src/features/home/home.types.ts +++ b/src/frontend/packages/core/src/features/home/home.types.ts @@ -2,7 +2,7 @@ import { Observable } from 'rxjs'; import { HomeCardShortcut } from '../../../../store/src/entity-catalog/entity-catalog.types'; import { EndpointModel } from '../../../../store/src/public-api'; -import { IHydrationResults } from '../../../../store/src/types/user-favorite-manager.types'; +import { UserFavorite } from './../../../../store/src/types/user-favorites.types'; // Layout for a home page card @@ -23,6 +23,6 @@ export abstract class HomePageEndpointCard { } export interface LinkMetadata { - favs: IHydrationResults[]; + favs: UserFavorite[]; shortcuts: HomeCardShortcut[]; } diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html index cb1e36637a..20a444e35c 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html @@ -1,10 +1,10 @@ - +
{{ icon.icon }}
-
{{ config.name }}
-
{{ prettyName }}
+
{{ name }}
+
{{ favoriteType }}
diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index 6d4eeef216..89911f6ff4 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -14,7 +14,6 @@ import { StratosEndpointExtensionDefinition, } from '../../../store/src/entity-catalog/entity-catalog.types'; import { EndpointAuthTypeConfig, EndpointType } from '../../../store/src/extension-types'; -import { FavoritesConfigMapper } from '../../../store/src/favorite-config-mapper'; import { metricEntityType } from '../../../store/src/helpers/stratos-entity-factory'; import { IFavoriteMetadata, UserFavorite } from '../../../store/src/types/user-favorites.types'; import { UserFavoriteManager } from '../../../store/src/user-favorite-manager'; From 8ce178f44d34bb02963ff35514eb8b2d21a2fa78 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 17 Nov 2020 08:20:22 +0000 Subject: [PATCH 58/74] Fix unit tests --- .../user-favorites-groups.reducer.spec.ts | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.spec.ts b/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.spec.ts index 3afec96484..ad3370c52f 100644 --- a/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.spec.ts +++ b/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.spec.ts @@ -60,9 +60,8 @@ describe('userFavoritesReducer', () => { ...defaultState, groups: { [endpointFav.guid]: { + endpoint: new UserFavorite('endpoint1', 'cf', 'endpoint'), ethereal: false, - search: null, - typeFilter: null, entitiesIds: [] } } @@ -79,9 +78,8 @@ describe('userFavoritesReducer', () => { ...defaultState, groups: { [endpointFav.guid]: { + endpoint: {}, ethereal: true, - search: null, - typeFilter: null, entitiesIds: [ fav.guid ] @@ -100,9 +98,8 @@ describe('userFavoritesReducer', () => { ...defaultState, groups: { [endpointFav.guid]: { + endpoint: {} as UserFavorite, ethereal: false, - search: null, - typeFilter: null, entitiesIds: [] } } @@ -114,9 +111,8 @@ describe('userFavoritesReducer', () => { ...defaultState, groups: { [endpointFav.guid]: { + endpoint: {}, ethereal: false, - search: null, - typeFilter: null, entitiesIds: [ fav.guid ] @@ -151,26 +147,23 @@ describe('userFavoritesReducer', () => { ...defaultState, groups: { [endpointFav.guid]: { + endpoint: new UserFavorite('endpoint1', 'cf', 'endpoint'), ethereal: false, - search: null, - typeFilter: null, entitiesIds: [ fav.guid ] }, [endpointFav2.guid]: { + endpoint: new UserFavorite('endpoint2', 'cf', 'endpoint'), ethereal: false, - search: null, - typeFilter: null, entitiesIds: [ fav2.guid, fav21.guid ] }, [endpoint3Fav.guid]: { + endpoint: {}, ethereal: true, - search: null, - typeFilter: null, entitiesIds: [ fav3.guid ] @@ -188,9 +181,8 @@ describe('userFavoritesReducer', () => { ...defaultState, groups: { [endpointFav.guid]: { + endpoint: {} as UserFavorite, ethereal: false, - search: null, - typeFilter: null, entitiesIds: [ fav.guid ] @@ -202,9 +194,8 @@ describe('userFavoritesReducer', () => { ...defaultState, groups: { [endpointFav.guid]: { + endpoint: {}, ethereal: false, - search: null, - typeFilter: null, entitiesIds: [ ] } @@ -221,9 +212,8 @@ describe('userFavoritesReducer', () => { ...defaultState, groups: { [endpointFav.guid]: { + endpoint: {} as UserFavorite, ethereal: true, - search: null, - typeFilter: null, entitiesIds: [ fav.guid ] From 73322d00c0927af6e35040e9cc76aac999c28546 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 17 Nov 2020 16:41:26 +0000 Subject: [PATCH 59/74] Fix lint issue --- .../cloud-foundry/src/shared/data-services/cf-user.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/packages/cloud-foundry/src/shared/data-services/cf-user.service.ts b/src/frontend/packages/cloud-foundry/src/shared/data-services/cf-user.service.ts index 2fef14f74b..7e3bf67940 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/data-services/cf-user.service.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/data-services/cf-user.service.ts @@ -458,7 +458,7 @@ export class CfUserService { ); }; - private createSpaceGetUsersAction = (isAdmin: boolean, cfGuid: string, spaceGuid: string,): PaginatedAction => { + private createSpaceGetUsersAction = (isAdmin: boolean, cfGuid: string, spaceGuid: string): PaginatedAction => { return cfEntityCatalog.user.actions.getAllInSpace( spaceGuid, cfGuid, From 885d582f1a41e3a95cfd8365ee52504a583b4a9b Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 17 Nov 2020 20:53:05 +0000 Subject: [PATCH 60/74] Fix lint issue --- .../cloud-foundry/src/shared/data-services/cf-user.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/packages/cloud-foundry/src/shared/data-services/cf-user.service.ts b/src/frontend/packages/cloud-foundry/src/shared/data-services/cf-user.service.ts index 2fef14f74b..7e3bf67940 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/data-services/cf-user.service.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/data-services/cf-user.service.ts @@ -458,7 +458,7 @@ export class CfUserService { ); }; - private createSpaceGetUsersAction = (isAdmin: boolean, cfGuid: string, spaceGuid: string,): PaginatedAction => { + private createSpaceGetUsersAction = (isAdmin: boolean, cfGuid: string, spaceGuid: string): PaginatedAction => { return cfEntityCatalog.user.actions.getAllInSpace( spaceGuid, cfGuid, From 34cf9cd7d9dcb6de8a924256e0aa202181c2477a Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Wed, 18 Nov 2020 07:36:19 +0000 Subject: [PATCH 61/74] Fix unit test --- .../default-endpoint-home-component.component.spec.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.spec.ts b/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.spec.ts index 347193314a..a8c5ca650c 100644 --- a/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.spec.ts +++ b/src/frontend/packages/core/src/features/home/home/default-endpoint-home-component/default-endpoint-home-component.component.spec.ts @@ -1,20 +1,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { DefaultEndpointHomeComponentComponent } from './default-endpoint-home-component.component'; +import { DefaultEndpointHomeComponent } from './default-endpoint-home-component.component'; describe('DefaultEndpointHomeComponentComponent', () => { - let component: DefaultEndpointHomeComponentComponent; - let fixture: ComponentFixture; + let component: DefaultEndpointHomeComponent; + let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ DefaultEndpointHomeComponentComponent ] + declarations: [ DefaultEndpointHomeComponent ] }) .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(DefaultEndpointHomeComponentComponent); + fixture = TestBed.createComponent(DefaultEndpointHomeComponent); component = fixture.componentInstance; fixture.detectChanges(); }); From cd23257bed3955427a1c78fb1944f1500660e82b Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 23 Nov 2020 11:49:10 +0000 Subject: [PATCH 62/74] Fix merge issue --- src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index 9a363c2375..388bdff1f8 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -1,7 +1,6 @@ import { Compiler, Injector } from '@angular/core'; import { Action, Store } from '@ngrx/store'; import moment from 'moment'; -import { entityFetchedWithoutError } from '@stratosui/store'; import { combineLatest, Observable, of } from 'rxjs'; import { first, map } from 'rxjs/operators'; From 5eb6eb068acd28a8b0560a45f32b9b9545ee4083 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 23 Nov 2020 12:06:43 +0000 Subject: [PATCH 63/74] Fix merge issue --- .../cloud-foundry/src/cf-entity-generator.ts | 45 ++++++++----------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index 388bdff1f8..818cd09268 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -492,7 +492,7 @@ function generateCFAppEnvVarEntity(endpointDefinition: StratosEndpointExtensionD name: `Application environment variables (${ent.metadata.guid}).`, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid }, }); return cfEntityCatalog.appEnvVar; @@ -517,7 +517,7 @@ function generateCFAppSummaryEntity(endpointDefinition: StratosEndpointExtension name: ent.name, guid: ent.guid }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.guid, } }); return cfEntityCatalog.appSummary; @@ -579,7 +579,7 @@ function generateCFInfoEntity(endpointDefinition: StratosEndpointExtensionDefini guid: info.entity.name, name: info.entity.name, }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid } } ); @@ -611,7 +611,7 @@ function generateCFUserProvidedServiceInstanceEntity(endpointDefinition: Stratos name: ent.entity.name, guid: ent.metadata.guid, }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid }, } ); @@ -660,7 +660,7 @@ function generateCFAppStatsEntity(endpointDefinition: StratosEndpointExtensionDe name: ent.guid, guid: ent.guid }), - getGuid: metadata => metadata.name, + getGuid: entity => entity.guid } }); return cfEntityCatalog.appStats; @@ -765,7 +765,7 @@ function generateCFServiceBindingEntity(endpointDefinition: StratosEndpointExten name: ent.metadata.guid, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid } } ); @@ -796,7 +796,7 @@ function generateCFServiceEntity(endpointDefinition: StratosEndpointExtensionDef name: ent.entity.label, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid }, } ); @@ -827,7 +827,7 @@ function generateCFServicePlanEntity(endpointDefinition: StratosEndpointExtensio name: ent.entity.name, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid } } ); @@ -863,7 +863,7 @@ function generateCFServiceInstanceEntity(endpointDefinition: StratosEndpointExte name: ent.entity.name, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid } } ); @@ -888,7 +888,7 @@ function generateCFUserEntity(endpointDefinition: StratosEndpointExtensionDefini name: ent.entity.username || ent.entity.guid || ent.metadata.guid, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid } } ); @@ -919,7 +919,7 @@ function generateCFDomainEntity(endpointDefinition: StratosEndpointExtensionDefi name: ent.entity.name, guid: ent.metadata.guid }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid } } ); @@ -953,7 +953,7 @@ function generateEventEntity(endpointDefinition: StratosEndpointExtensionDefinit name: event.metadata.guid, }; }, - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid } } ); @@ -985,7 +985,7 @@ function generateRouteEntity(endpointDefinition: StratosEndpointExtensionDefinit guid: app.metadata.guid, name: app.entity.domain_url, }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid } } ); @@ -1016,7 +1016,7 @@ function generateStackEntity(endpointDefinition: StratosEndpointExtensionDefinit guid: app.metadata.guid, name: app.entity.name, }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid } } ); @@ -1065,7 +1065,7 @@ function generateFeatureFlagEntity(endpointDefinition: StratosEndpointExtensionD guid: ff.guid, name: ff.name, }), - getGuid: metadata => metadata.guid, + getGuid: entity => entity.guid, } } ); @@ -1115,10 +1115,7 @@ function generateCfApplicationEntity(endpointDefinition: StratosEndpointExtensio name: app.entity.name, }), getLink: metadata => `/applications/${metadata.cfGuid}/${metadata.guid}/summary`, - getGuid: metadata => metadata.guid, - getLines: () => ([ - ['Created', (meta) => meta.createdAt] - ]) + getGuid: entity => entity.metadata.guid }, actionBuilders: applicationActionBuilder }, @@ -1156,11 +1153,8 @@ function generateCfSpaceEntity(endpointDefinition: StratosEndpointExtensionDefin cfGuid: space.entity.cfGuid, createdAt: moment(space.metadata.created_at).format('LLL'), }), - getLines: () => ([ - ['Created', (meta) => meta.createdAt] - ]), getLink: metadata => `/cloud-foundry/${metadata.cfGuid}/organizations/${metadata.orgGuid}/spaces/${metadata.guid}/summary`, - getGuid: metadata => metadata.guid, + getGuid: entity => entity.metadata.guid } } ); @@ -1200,10 +1194,7 @@ function generateCfOrgEntity(endpointDefinition: StratosEndpointExtensionDefinit createdAt: moment(org.metadata.created_at).format('LLL'), }), getLink: metadata => `/cloud-foundry/${metadata.cfGuid}/organizations/${metadata.guid}`, - getLines: () => ([ - ['Created', (meta) => meta.createdAt] - ]), - getGuid: metadata => metadata.guid + getGuid: entity => entity.metadata.guid } } ); From c8928e27aa64b4c02f1132eac403b5a84a63b822 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 23 Nov 2020 13:17:10 +0000 Subject: [PATCH 64/74] Fix kube favorites not showing and sizing issue --- .../home-page-endpoint-card.component.ts | 3 ++- .../core/src/features/home/home/home-page.component.scss | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index 3bcdcc45bc..8c54c4b6e7 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -140,7 +140,8 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi filter(([favs, layout]) => !!layout), map(([favs, layout]) => { // Get the list of shortcuts for the endpoint for the given endpoint ID - const allShortcuts = this.definition?.homeCard?.shortcuts(this.endpoint.guid) || []; + const shortcutsFn = this.definition?.homeCard?.shortcuts; + const allShortcuts = shortcutsFn ? shortcutsFn(this.endpoint.guid) || [] : []; let shortcuts = allShortcuts; const max = (layout.y > 1) ? MAX_FAVS_COMPACT : MAX_FAVS_NORMAL; const totalShortcuts = allShortcuts.length; diff --git a/src/frontend/packages/core/src/features/home/home/home-page.component.scss b/src/frontend/packages/core/src/features/home/home/home-page.component.scss index 81e0860654..f2b06fd032 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page.component.scss +++ b/src/frontend/packages/core/src/features/home/home/home-page.component.scss @@ -1,7 +1,12 @@ +:host { + display: flex; +} + .home-page { display: flex; flex-direction: column; height: 100%; + width: 100%; &__header { margin-top: 0; } From 203db49297bc9133b1b8e6a3246d8cc02098e596 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 23 Nov 2020 13:44:35 +0000 Subject: [PATCH 65/74] Fix kube endpoint card shortcuts --- .../home/kubernetes-home-card.component.ts | 12 ++++---- .../services/kubernetes-endpoint.service.ts | 30 +++++++++++++++---- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts index 2279a142cb..bf615dad5e 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/home/kubernetes-home-card.component.ts @@ -1,4 +1,6 @@ import { Component, Input, OnInit } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { AppState } from 'frontend/packages/store/src/app-state'; import { combineLatest, Observable } from 'rxjs'; import { first, map } from 'rxjs/operators'; @@ -35,14 +37,10 @@ export class KubernetesHomeCardComponent implements OnInit { public nodeCount$: Observable; public namespaceCount$: Observable; - constructor(private kubeEndpointService: KubernetesEndpointService) { } + constructor(private store: Store) { } ngOnInit() { const guid = this.endpoint.guid; - if (guid) { - this.kubeEndpointService.initialize(this.endpoint.guid); - } - this.shortcuts = [ { title: 'View Nodes', @@ -73,7 +71,7 @@ export class KubernetesHomeCardComponent implements OnInit { this.nodeCount$ = nodes$.pipe(map(entities => entities.length)); this.namespaceCount$ = namespaces$.pipe(map(entities => entities.length)); - this.kubeEndpointService.kubeTerminalEnabled$.pipe(first()).subscribe(hasKubeTerminal => { + KubernetesEndpointService.hasKubeTerminalEnabled(this.store).pipe(first()).subscribe(hasKubeTerminal => { if (hasKubeTerminal) { this.shortcuts.push( { @@ -86,7 +84,7 @@ export class KubernetesHomeCardComponent implements OnInit { } }); - this.kubeEndpointService.kubeDashboardConfigured$.pipe(first()).subscribe(hasKubeDashboard => { + KubernetesEndpointService.kubeDashboardConfigured(this.store, guid).pipe(first()).subscribe(hasKubeDashboard => { if (hasKubeDashboard) { this.shortcuts.push( { diff --git a/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-endpoint.service.ts b/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-endpoint.service.ts index b33f03a1f1..3195fd4898 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-endpoint.service.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-endpoint.service.ts @@ -65,6 +65,30 @@ export class KubernetesEndpointService { kubeDashboardConfigured$: Observable; kubeTerminalEnabled$: Observable; + public static hasKubeTerminalEnabled(store: Store): Observable { + return store.select('auth').pipe( + filter(auth => !!auth.sessionData['plugin-config']), + map(auth => auth.sessionData['plugin-config'].kubeTerminalEnabled === 'true') + ); + } + + public static kubeDashboardConfigured(store: Store, kubeGuid: string): Observable { + const kubeDashboardEnabled$ = store.select('auth').pipe( + filter(auth => !!auth.sessionData['plugin-config']), + map(auth => auth.sessionData['plugin-config'].kubeDashboardEnabled === 'true') + ); + + const kubeDashboardStatus$ = kubeEntityCatalog.dashboard.store.getEntityService(kubeGuid).waitForEntity$.pipe( + map(status => status.entity), + filter(status => !!status) + ); + + return kubeDashboardEnabled$.pipe( + switchMap(enabled => enabled ? kubeDashboardStatus$ : of(null)), + map(status => status && status.installed && !!status.serviceAccount && !!status.service), + ); + } + constructor( public baseKube: BaseKubeGuid, private store: Store, @@ -235,10 +259,7 @@ export class KubernetesEndpointService { this.services$ = this.getObservable(kubeEntityCatalog.service.store.getPaginationService(this.kubeGuid)); - this.kubeDashboardEnabled$ = this.store.select('auth').pipe( - filter(auth => !!auth.sessionData['plugin-config']), - map(auth => auth.sessionData['plugin-config'].kubeDashboardEnabled === 'true') - ); + this.kubeDashboardEnabled$ = KubernetesEndpointService.hasKubeTerminalEnabled(this.store); this.kubeTerminalEnabled$ = this.store.select('auth').pipe( filter(auth => !!auth.sessionData['plugin-config']), @@ -281,5 +302,4 @@ export class KubernetesEndpointService { private getObservable(obs: PaginationObservables): Observable { return obs.entities$.pipe(filter(p => !!p), first()); } - } From 40d21646c05383e89511c61cbcd5431fc30a3fb7 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 23 Nov 2020 14:10:16 +0000 Subject: [PATCH 66/74] Reinstate entity validation checks for CF app/org/space --- .../cloud-foundry/src/cf-entity-generator.ts | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index 818cd09268..2764e49b1d 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -1115,7 +1115,8 @@ function generateCfApplicationEntity(endpointDefinition: StratosEndpointExtensio name: app.entity.name, }), getLink: metadata => `/applications/${metadata.cfGuid}/${metadata.guid}/summary`, - getGuid: entity => entity.metadata.guid + getGuid: entity => entity.metadata.guid, + getIsValid: (metadata) => cfEntityCatalog.application.api.get(metadata.guid, metadata.cfGuid, {}).pipe(entityFetchedWithoutError()) }, actionBuilders: applicationActionBuilder }, @@ -1154,7 +1155,8 @@ function generateCfSpaceEntity(endpointDefinition: StratosEndpointExtensionDefin createdAt: moment(space.metadata.created_at).format('LLL'), }), getLink: metadata => `/cloud-foundry/${metadata.cfGuid}/organizations/${metadata.orgGuid}/spaces/${metadata.guid}/summary`, - getGuid: entity => entity.metadata.guid + getGuid: entity => entity.metadata.guid, + getIsValid: (metadata) => cfEntityCatalog.space.api.get(metadata.guid, metadata.cfGuid).pipe(entityFetchedWithoutError()) } } ); @@ -1188,26 +1190,19 @@ function generateCfOrgEntity(endpointDefinition: StratosEndpointExtensionDefinit entityBuilder: { getMetadata: org => ({ guid: org.metadata.guid, - status: getOrgStatus(org), name: org.entity.name, cfGuid: org.entity.cfGuid, createdAt: moment(org.metadata.created_at).format('LLL'), }), getLink: metadata => `/cloud-foundry/${metadata.cfGuid}/organizations/${metadata.guid}`, - getGuid: entity => entity.metadata.guid + getGuid: entity => entity.metadata.guid, + getIsValid: (metadata) => cfEntityCatalog.org.api.get(metadata.guid, metadata.cfGuid, {}).pipe(entityFetchedWithoutError()) } } ); return cfEntityCatalog.org; } -function getOrgStatus(org: APIResource) { - if (!org || !org.entity || !org.entity.status) { - return 'Unknown'; - } - return org.entity.status.charAt(0).toUpperCase() + org.entity.status.slice(1); -} - function generateCFMetrics(endpointDefinition: StratosEndpointExtensionDefinition) { const definition: IStratosEntityDefinition = { type: metricEntityType, From 3160d6f886b826d99de3376ece85ea10d58f0062 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 23 Nov 2020 14:21:54 +0000 Subject: [PATCH 67/74] Fix favorite validation --- src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts | 1 + .../favorites-meta-card/favorites-meta-card.component.html | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index 2764e49b1d..50831559f2 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -1,5 +1,6 @@ import { Compiler, Injector } from '@angular/core'; import { Action, Store } from '@ngrx/store'; +import { entityFetchedWithoutError } from '@stratosui/store'; import moment from 'moment'; import { combineLatest, Observable, of } from 'rxjs'; import { first, map } from 'rxjs/operators'; diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html index 20a444e35c..9eecffee4d 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.html @@ -1,4 +1,4 @@ - +
{{ icon.icon }}
@@ -6,5 +6,6 @@
{{ name }}
{{ favoriteType }}
+ warning From 4b9a2d5c4e707d192b1f6b75f794b56d2d2893f0 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 24 Nov 2020 08:10:24 +0000 Subject: [PATCH 68/74] Tidy ups --- .../src/store/autoscaler-entity-generator.ts | 3 +- .../cloud-foundry/src/cf-entity-catalog.ts | 53 +++++------ .../cloud-foundry/src/cf-entity-generator.ts | 92 +++++++------------ .../cloud-foundry/src/cf-metadata-types.ts | 20 ---- .../application-tabs-base.component.ts | 4 +- ...oud-foundry-organization-base.component.ts | 7 +- .../list-types/app/card/card-app.component.ts | 5 +- .../core/src/core/endpoints.service.ts | 11 ++- .../favorites-meta-card.component.ts | 2 +- .../endpoint-card/endpoint-card.component.ts | 4 +- .../kubernetes/kubernetes-entity-generator.ts | 4 +- .../kubernetes-namespace.component.ts | 4 +- .../entity-catalog-entity.ts | 9 +- .../entity-catalog/entity-catalog.types.ts | 4 +- .../store/src/types/user-favorites.types.ts | 6 +- 15 files changed, 91 insertions(+), 137 deletions(-) diff --git a/src/frontend/packages/cf-autoscaler/src/store/autoscaler-entity-generator.ts b/src/frontend/packages/cf-autoscaler/src/store/autoscaler-entity-generator.ts index 4e71099074..a4b5d860bb 100644 --- a/src/frontend/packages/cf-autoscaler/src/store/autoscaler-entity-generator.ts +++ b/src/frontend/packages/cf-autoscaler/src/store/autoscaler-entity-generator.ts @@ -1,4 +1,3 @@ -import { IOrgFavMetadata } from '../../../cloud-foundry/src/cf-metadata-types'; import { StratosBaseCatalogEntity, StratosCatalogEntity, @@ -114,5 +113,5 @@ function generateMetricEntity(endpointDefinition: IStratosEndpointDefinition) { labelPlural: 'Autoscaler Metrics', endpoint: endpointDefinition, }; - return new StratosCatalogEntity>(definition); + return new StratosCatalogEntity>(definition); } diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-catalog.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-catalog.ts index 6485e4e8e1..53317cbeb9 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-catalog.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-catalog.ts @@ -3,6 +3,7 @@ import { StratosCatalogEndpointEntity, } from '../../store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity'; import { APIResource } from '../../store/src/types/api.types'; +import { IFavoriteMetadata } from '../../store/src/types/user-favorites.types'; import { IService, IServiceBinding, @@ -29,7 +30,7 @@ import { ISpaceQuotaDefinition, IStack, } from './cf-api.types'; -import { IAppFavMetadata, IBasicCFMetaData, IOrgFavMetadata, ISpaceFavMetadata } from './cf-metadata-types'; +import { ISpaceFavMetadata } from './cf-metadata-types'; import { AppEnvVarActionBuilders } from './entity-action-builders/application-env-var.action-builders'; import { AppStatsActionBuilders } from './entity-action-builders/application-stats.action-builders'; import { AppSummaryActionBuilders } from './entity-action-builders/application-summary.action-builders'; @@ -65,132 +66,132 @@ export class CfEntityCatalog { public cfEndpoint: StratosCatalogEndpointEntity; public quotaDefinition: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, QuotaDefinitionActionBuilder >; public appEnvVar: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, AppEnvVarActionBuilders >; public appSummary: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, IAppSummary, AppSummaryActionBuilders >; public spaceQuota: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, SpaceQuotaDefinitionActionBuilders >; public privateDomain: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource >; public cfInfo: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, CfInfoDefinitionActionBuilders >; public appStats: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, AppStat, AppStatsActionBuilders >; public buildPack: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, BuildpackActionBuilders >; public serviceBroker: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, ServiceBrokerActionBuilders >; public servicePlanVisibility: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, ServicePlanVisibilityActionBuilders >; public securityGroup: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, SecurityGroupBuilders >; public serviceBinding: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, ServiceBindingActionBuilders >; public service: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, ServiceActionBuilders >; public servicePlan: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, ServicePlanActionBuilders >; public serviceInstance: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, ServiceInstanceActionBuilders >; public user: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, UserActionBuilders >; public domain: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, DomainActionBuilders >; public event: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, CfEventActionBuilders >; public route: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, RoutesActionBuilders >; public stack: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, StackActionBuilders >; public featureFlag: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, IFeatureFlag, FeatureFlagActionBuilders >; public application: StratosBaseCatalogEntity< - IAppFavMetadata, + IFavoriteMetadata, APIResource, ApplicationActionBuilders >; @@ -202,17 +203,17 @@ export class CfEntityCatalog { >; public org: StratosBaseCatalogEntity< - IOrgFavMetadata, + IFavoriteMetadata, APIResource, OrganizationActionBuilders >; public metric: StratosBaseCatalogEntity< - IBasicCFMetaData + IFavoriteMetadata >; public userProvidedService: StratosBaseCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, UserProvidedServiceActionBuilder >; diff --git a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts index 50831559f2..e2ecf98897 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-entity-generator.ts @@ -1,7 +1,6 @@ import { Compiler, Injector } from '@angular/core'; import { Action, Store } from '@ngrx/store'; import { entityFetchedWithoutError } from '@stratosui/store'; -import moment from 'moment'; import { combineLatest, Observable, of } from 'rxjs'; import { first, map } from 'rxjs/operators'; @@ -29,6 +28,7 @@ import { selectSessionData } from '../../store/src/reducers/auth.reducer'; import { APIResource, EntityInfo } from '../../store/src/types/api.types'; import { PaginatedAction, PaginationEntityState } from '../../store/src/types/pagination.types'; import { ICFAction } from '../../store/src/types/request.types'; +import { IFavoriteMetadata } from '../../store/src/types/user-favorites.types'; import { CfValidateEntitiesStart } from './actions/relations-actions'; import { IService, @@ -91,7 +91,7 @@ import { } from './cf-entity-types'; import { CfErrorResponse, getCfError } from './cf-error-helpers'; import { getFavoriteFromCfEntity } from './cf-favorites-helpers'; -import { IAppFavMetadata, IBasicCFMetaData, IOrgFavMetadata, ISpaceFavMetadata } from './cf-metadata-types'; +import { ISpaceFavMetadata } from './cf-metadata-types'; import { CF_ENDPOINT_TYPE } from './cf-types'; import { AppEnvVarActionBuilders, @@ -436,7 +436,7 @@ function generateCFQuotaDefinitionEntity(endpointDefinition: StratosEndpointExte labelPlural: 'Organization Quotas', }; cfEntityCatalog.quotaDefinition = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, QuotaDefinitionActionBuilder >(definition, { @@ -479,7 +479,7 @@ function generateCFAppEnvVarEntity(endpointDefinition: StratosEndpointExtensionD labelPlural: 'App Env Vars', }; cfEntityCatalog.appEnvVar = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, AppEnvVarActionBuilders, AppEnvVarActionBuilders @@ -491,7 +491,6 @@ function generateCFAppEnvVarEntity(endpointDefinition: StratosEndpointExtensionD entityBuilder: { getMetadata: ent => ({ name: `Application environment variables (${ent.metadata.guid}).`, - guid: ent.metadata.guid }), getGuid: entity => entity.metadata.guid }, @@ -507,7 +506,7 @@ function generateCFAppSummaryEntity(endpointDefinition: StratosEndpointExtension label: 'App Summary', labelPlural: 'App Summaries', }; - cfEntityCatalog.appSummary = new StratosCatalogEntity(definition, { + cfEntityCatalog.appSummary = new StratosCatalogEntity(definition, { dataReducers: [ updateAppSummaryRoutesReducer, endpointDisconnectRemoveEntitiesReducer() @@ -516,7 +515,6 @@ function generateCFAppSummaryEntity(endpointDefinition: StratosEndpointExtension entityBuilder: { getMetadata: ent => ({ name: ent.name, - guid: ent.guid }), getGuid: entity => entity.guid, } @@ -533,7 +531,7 @@ function generateCFSpaceQuotaEntity(endpointDefinition: StratosEndpointExtension labelPlural: 'Space Quotas', }; cfEntityCatalog.spaceQuota = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, SpaceQuotaDefinitionActionBuilders>(definition, { dataReducers: [ @@ -552,7 +550,7 @@ function generateCFPrivateDomainEntity(endpointDefinition: StratosEndpointExtens label: 'Private Domain', labelPlural: 'Private Domains', }; - cfEntityCatalog.privateDomain = new StratosCatalogEntity>(definition, { + cfEntityCatalog.privateDomain = new StratosCatalogEntity>(definition, { dataReducers: [ endpointDisconnectRemoveEntitiesReducer() ], @@ -568,7 +566,7 @@ function generateCFInfoEntity(endpointDefinition: StratosEndpointExtensionDefini labelPlural: 'Cloud Foundry Infos', endpoint: endpointDefinition }; - cfEntityCatalog.cfInfo = new StratosCatalogEntity, CfInfoDefinitionActionBuilders>( + cfEntityCatalog.cfInfo = new StratosCatalogEntity, CfInfoDefinitionActionBuilders>( cfInfoDefinition, { dataReducers: [ @@ -577,7 +575,6 @@ function generateCFInfoEntity(endpointDefinition: StratosEndpointExtensionDefini actionBuilders: cfInfoDefinitionActionBuilders, entityBuilder: { getMetadata: info => ({ - guid: info.entity.name, name: info.entity.name, }), getGuid: entity => entity.metadata.guid @@ -596,7 +593,7 @@ function generateCFUserProvidedServiceInstanceEntity(endpointDefinition: Stratos endpoint: endpointDefinition, }; cfEntityCatalog.userProvidedService = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, UserProvidedServiceActionBuilder >( @@ -610,7 +607,6 @@ function generateCFUserProvidedServiceInstanceEntity(endpointDefinition: Stratos entityBuilder: { getMetadata: ent => ({ name: ent.entity.name, - guid: ent.metadata.guid, }), getGuid: entity => entity.metadata.guid }, @@ -651,7 +647,7 @@ function generateCFAppStatsEntity(endpointDefinition: StratosEndpointExtensionDe return data; }, }; - cfEntityCatalog.appStats = new StratosCatalogEntity(definition, { + cfEntityCatalog.appStats = new StratosCatalogEntity(definition, { dataReducers: [ endpointDisconnectRemoveEntitiesReducer() ], @@ -659,7 +655,6 @@ function generateCFAppStatsEntity(endpointDefinition: StratosEndpointExtensionDe entityBuilder: { getMetadata: ent => ({ name: ent.guid, - guid: ent.guid }), getGuid: entity => entity.guid } @@ -673,7 +668,7 @@ function generateCFBuildPackEntity(endpointDefinition: StratosEndpointExtensionD schema: cfEntityFactory(buildpackEntityType), endpoint: endpointDefinition }; - cfEntityCatalog.buildPack = new StratosCatalogEntity, BuildpackActionBuilders>(definition, { + cfEntityCatalog.buildPack = new StratosCatalogEntity, BuildpackActionBuilders>(definition, { dataReducers: [ endpointDisconnectRemoveEntitiesReducer() ], @@ -689,7 +684,7 @@ function generateCFServiceBrokerEntity(endpointDefinition: StratosEndpointExtens endpoint: endpointDefinition }; cfEntityCatalog.serviceBroker = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, ServiceBrokerActionBuilders>(definition, { dataReducers: [ @@ -707,7 +702,7 @@ function generateCFServicePlanVisibilityEntity(endpointDefinition: StratosEndpoi endpoint: endpointDefinition }; cfEntityCatalog.servicePlanVisibility = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, ServicePlanVisibilityActionBuilders >(definition, { @@ -728,7 +723,7 @@ function generateCFSecurityGroupEntity(endpointDefinition: StratosEndpointExtens endpoint: endpointDefinition }; cfEntityCatalog.securityGroup = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, SecurityGroupBuilders>(definition, { dataReducers: [ @@ -751,7 +746,7 @@ function generateCFServiceBindingEntity(endpointDefinition: StratosEndpointExten endpoint: endpointDefinition }; cfEntityCatalog.serviceBinding = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, ServiceBindingActionBuilders >( @@ -764,7 +759,6 @@ function generateCFServiceBindingEntity(endpointDefinition: StratosEndpointExten entityBuilder: { getMetadata: ent => ({ name: ent.metadata.guid, - guid: ent.metadata.guid }), getGuid: entity => entity.metadata.guid } @@ -782,7 +776,7 @@ function generateCFServiceEntity(endpointDefinition: StratosEndpointExtensionDef endpoint: endpointDefinition }; cfEntityCatalog.service = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, ServiceActionBuilders >( @@ -795,7 +789,6 @@ function generateCFServiceEntity(endpointDefinition: StratosEndpointExtensionDef entityBuilder: { getMetadata: ent => ({ name: ent.entity.label, - guid: ent.metadata.guid }), getGuid: entity => entity.metadata.guid }, @@ -813,7 +806,7 @@ function generateCFServicePlanEntity(endpointDefinition: StratosEndpointExtensio endpoint: endpointDefinition }; cfEntityCatalog.servicePlan = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, ServicePlanActionBuilders >( @@ -826,7 +819,6 @@ function generateCFServicePlanEntity(endpointDefinition: StratosEndpointExtensio entityBuilder: { getMetadata: ent => ({ name: ent.entity.name, - guid: ent.metadata.guid }), getGuid: entity => entity.metadata.guid } @@ -848,7 +840,7 @@ function generateCFServiceInstanceEntity(endpointDefinition: StratosEndpointExte endpoint: endpointDefinition, }; cfEntityCatalog.serviceInstance = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, ServiceInstanceActionBuilders >( @@ -862,7 +854,6 @@ function generateCFServiceInstanceEntity(endpointDefinition: StratosEndpointExte entityBuilder: { getMetadata: ent => ({ name: ent.entity.name, - guid: ent.metadata.guid }), getGuid: entity => entity.metadata.guid } @@ -879,7 +870,7 @@ function generateCFUserEntity(endpointDefinition: StratosEndpointExtensionDefini labelPlural: 'Users', endpoint: endpointDefinition, }; - cfEntityCatalog.user = new StratosCatalogEntity, UserActionBuilders>( + cfEntityCatalog.user = new StratosCatalogEntity, UserActionBuilders>( definition, { actionBuilders: userActionBuilders, @@ -887,7 +878,6 @@ function generateCFUserEntity(endpointDefinition: StratosEndpointExtensionDefini entityBuilder: { getMetadata: ent => ({ name: ent.entity.username || ent.entity.guid || ent.metadata.guid, - guid: ent.metadata.guid }), getGuid: entity => entity.metadata.guid } @@ -905,7 +895,7 @@ function generateCFDomainEntity(endpointDefinition: StratosEndpointExtensionDefi endpoint: endpointDefinition }; cfEntityCatalog.domain = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, DomainActionBuilders >( @@ -918,7 +908,6 @@ function generateCFDomainEntity(endpointDefinition: StratosEndpointExtensionDefi entityBuilder: { getMetadata: ent => ({ name: ent.entity.name, - guid: ent.metadata.guid }), getGuid: entity => entity.metadata.guid } @@ -938,7 +927,7 @@ function generateEventEntity(endpointDefinition: StratosEndpointExtensionDefinit endpoint: endpointDefinition }; cfEntityCatalog.event = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, CfEventActionBuilders>( definition, @@ -950,7 +939,6 @@ function generateEventEntity(endpointDefinition: StratosEndpointExtensionDefinit entityBuilder: { getMetadata: event => { return { - guid: event.metadata.guid, name: event.metadata.guid, }; }, @@ -970,7 +958,7 @@ function generateRouteEntity(endpointDefinition: StratosEndpointExtensionDefinit endpoint: endpointDefinition }; cfEntityCatalog.route = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, RoutesActionBuilders >( @@ -983,7 +971,6 @@ function generateRouteEntity(endpointDefinition: StratosEndpointExtensionDefinit ], entityBuilder: { getMetadata: app => ({ - guid: app.metadata.guid, name: app.entity.domain_url, }), getGuid: entity => entity.metadata.guid @@ -1002,7 +989,7 @@ function generateStackEntity(endpointDefinition: StratosEndpointExtensionDefinit endpoint: endpointDefinition }; cfEntityCatalog.stack = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, APIResource, StackActionBuilders >( @@ -1014,7 +1001,6 @@ function generateStackEntity(endpointDefinition: StratosEndpointExtensionDefinit actionBuilders: stackActionBuilders, entityBuilder: { getMetadata: app => ({ - guid: app.metadata.guid, name: app.entity.name, }), getGuid: entity => entity.metadata.guid @@ -1052,7 +1038,7 @@ function generateFeatureFlagEntity(endpointDefinition: StratosEndpointExtensionD } }; cfEntityCatalog.featureFlag = new StratosCatalogEntity< - IBasicCFMetaData, + IFavoriteMetadata, IFeatureFlag, FeatureFlagActionBuilders>( featureFlagDefinition, @@ -1063,7 +1049,6 @@ function generateFeatureFlagEntity(endpointDefinition: StratosEndpointExtensionD actionBuilders: featureFlagActionBuilders, entityBuilder: { getMetadata: ff => ({ - guid: ff.guid, name: ff.name, }), getGuid: entity => entity.guid, @@ -1076,7 +1061,7 @@ function generateFeatureFlagEntity(endpointDefinition: StratosEndpointExtensionD function generateCfEndpointEntity(endpointDefinition: StratosEndpointExtensionDefinition) { cfEntityCatalog.cfEndpoint = new StratosCatalogEndpointEntity( endpointDefinition, - metadata => `/cloud-foundry/${metadata.guid}` + favorite => `/cloud-foundry/${favorite.endpointId}` ); return cfEntityCatalog.cfEndpoint; } @@ -1098,7 +1083,7 @@ function generateCfApplicationEntity(endpointDefinition: StratosEndpointExtensio }; cfEntityCatalog.application = new StratosCatalogEntity< - IAppFavMetadata, + IFavoriteMetadata, APIResource, ApplicationActionBuilders >( @@ -1110,14 +1095,11 @@ function generateCfApplicationEntity(endpointDefinition: StratosEndpointExtensio ], entityBuilder: { getMetadata: app => ({ - guid: app.metadata.guid, - cfGuid: app.entity.cfGuid, - createdAt: moment(app.metadata.created_at).format('LLL'), name: app.entity.name, }), - getLink: metadata => `/applications/${metadata.cfGuid}/${metadata.guid}/summary`, + getLink: favorite => `/applications/${favorite.endpointId}/${favorite.entityId}/summary`, getGuid: entity => entity.metadata.guid, - getIsValid: (metadata) => cfEntityCatalog.application.api.get(metadata.guid, metadata.cfGuid, {}).pipe(entityFetchedWithoutError()) + getIsValid: (fav) => cfEntityCatalog.application.api.get(fav.entityId, fav.endpointId, {}).pipe(entityFetchedWithoutError()) }, actionBuilders: applicationActionBuilder }, @@ -1149,15 +1131,12 @@ function generateCfSpaceEntity(endpointDefinition: StratosEndpointExtensionDefin ], entityBuilder: { getMetadata: space => ({ - guid: space.metadata.guid, orgGuid: space.entity.organization_guid ? space.entity.organization_guid : space.entity.organization.metadata.guid, name: space.entity.name, - cfGuid: space.entity.cfGuid, - createdAt: moment(space.metadata.created_at).format('LLL'), }), - getLink: metadata => `/cloud-foundry/${metadata.cfGuid}/organizations/${metadata.orgGuid}/spaces/${metadata.guid}/summary`, + getLink: favorite => `/cloud-foundry/${favorite.endpointId}/organizations/${favorite.metadata.orgGuid}/spaces/${favorite.entityId}/summary`, getGuid: entity => entity.metadata.guid, - getIsValid: (metadata) => cfEntityCatalog.space.api.get(metadata.guid, metadata.cfGuid).pipe(entityFetchedWithoutError()) + getIsValid: (fav) => cfEntityCatalog.space.api.get(fav.entityId, fav.endpointId).pipe(entityFetchedWithoutError()) } } ); @@ -1175,7 +1154,7 @@ function generateCfOrgEntity(endpointDefinition: StratosEndpointExtensionDefinit iconFont: 'stratos-icons' }; cfEntityCatalog.org = new StratosCatalogEntity< - IOrgFavMetadata, + IFavoriteMetadata, APIResource, OrganizationActionBuilders >( @@ -1190,14 +1169,11 @@ function generateCfOrgEntity(endpointDefinition: StratosEndpointExtensionDefinit ], entityBuilder: { getMetadata: org => ({ - guid: org.metadata.guid, name: org.entity.name, - cfGuid: org.entity.cfGuid, - createdAt: moment(org.metadata.created_at).format('LLL'), }), - getLink: metadata => `/cloud-foundry/${metadata.cfGuid}/organizations/${metadata.guid}`, + getLink: favorite => `/cloud-foundry/${favorite.endpointId}/organizations/${favorite.entityId}`, getGuid: entity => entity.metadata.guid, - getIsValid: (metadata) => cfEntityCatalog.org.api.get(metadata.guid, metadata.cfGuid, {}).pipe(entityFetchedWithoutError()) + getIsValid: (favorite) => cfEntityCatalog.org.api.get(favorite.entityId, favorite.endpointId, {}).pipe(entityFetchedWithoutError()) } } ); @@ -1212,7 +1188,7 @@ function generateCFMetrics(endpointDefinition: StratosEndpointExtensionDefinitio labelPlural: 'CF Metrics', endpoint: endpointDefinition, }; - cfEntityCatalog.metric = new StratosCatalogEntity( + cfEntityCatalog.metric = new StratosCatalogEntity( definition, { dataReducers: [ diff --git a/src/frontend/packages/cloud-foundry/src/cf-metadata-types.ts b/src/frontend/packages/cloud-foundry/src/cf-metadata-types.ts index 7f2889580c..f9beed8b62 100644 --- a/src/frontend/packages/cloud-foundry/src/cf-metadata-types.ts +++ b/src/frontend/packages/cloud-foundry/src/cf-metadata-types.ts @@ -1,25 +1,5 @@ import { IFavoriteMetadata } from '../../store/src/types/user-favorites.types'; export interface ISpaceFavMetadata extends IFavoriteMetadata { - guid: string; orgGuid: string; - name: string; - cfGuid: string; -} - -export interface IOrgFavMetadata extends IFavoriteMetadata { - guid: string; - name: string; - cfGuid: string; -} - -export interface IAppFavMetadata extends IFavoriteMetadata { - guid: string; - cfGuid: string; - name: string; -} - -export interface IBasicCFMetaData extends IFavoriteMetadata { - guid: string; - name: string; } diff --git a/src/frontend/packages/cloud-foundry/src/features/applications/application/application-tabs-base/application-tabs-base.component.ts b/src/frontend/packages/cloud-foundry/src/features/applications/application/application-tabs-base/application-tabs-base.component.ts index fc33ac304c..d12da834f3 100644 --- a/src/frontend/packages/cloud-foundry/src/features/applications/application/application-tabs-base/application-tabs-base.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/applications/application/application-tabs-base/application-tabs-base.component.ts @@ -6,7 +6,6 @@ import { filter, first, map, startWith, switchMap, withLatestFrom } from 'rxjs/o import { CFAppState } from '../../../../../../cloud-foundry/src/cf-app-state'; import { applicationEntityType } from '../../../../../../cloud-foundry/src/cf-entity-types'; -import { IAppFavMetadata } from '../../../../../../cloud-foundry/src/cf-metadata-types'; import { EndpointsService } from '../../../../../../core/src/core/endpoints.service'; import { getActionsFromExtensions, @@ -26,6 +25,7 @@ import { ActionState } from '../../../../../../store/src/reducers/api-request-re import { endpointEntitiesSelector } from '../../../../../../store/src/selectors/endpoint.selectors'; import { APIResource } from '../../../../../../store/src/types/api.types'; import { EndpointModel } from '../../../../../../store/src/types/endpoint.types'; +import { IFavoriteMetadata } from '../../../../../../store/src/types/user-favorites.types'; import { getFavoriteFromEntity } from '../../../../../../store/src/user-favorite-helpers'; import { UserFavoriteManager } from '../../../../../../store/src/user-favorite-manager'; import { UpdateExistingApplication } from '../../../../actions/application.actions'; @@ -48,7 +48,7 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { public favorite$ = this.applicationService.app$.pipe( filter(app => !!app), - map(app => getFavoriteFromEntity(app.entity, applicationEntityType, this.userFavoriteManager, CF_ENDPOINT_TYPE)) + map(app => getFavoriteFromEntity(app.entity, applicationEntityType, this.userFavoriteManager, CF_ENDPOINT_TYPE)) ); isBusyUpdating$: Observable<{ updating: boolean; }>; diff --git a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-base/cloud-foundry-organization-base.component.ts b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-base/cloud-foundry-organization-base.component.ts index 22788d8d9d..d9d7bc4ee5 100644 --- a/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-base/cloud-foundry-organization-base.component.ts +++ b/src/frontend/packages/cloud-foundry/src/features/cf/tabs/cf-organizations/cf-organization-base/cloud-foundry-organization-base.component.ts @@ -13,12 +13,11 @@ import { environment } from '../../../../../../../core/src/environments/environm import { IPageSideNavTab } from '../../../../../../../core/src/features/dashboard/page-side-nav/page-side-nav.component'; import { IHeaderBreadcrumb } from '../../../../../../../core/src/shared/components/page-header/page-header.types'; import { EntitySchema } from '../../../../../../../store/src/helpers/entity-schema'; -import { UserFavorite } from '../../../../../../../store/src/types/user-favorites.types'; +import { IFavoriteMetadata, UserFavorite } from '../../../../../../../store/src/types/user-favorites.types'; import { getFavoriteFromEntity } from '../../../../../../../store/src/user-favorite-helpers'; import { UserFavoriteManager } from '../../../../../../../store/src/user-favorite-manager'; import { cfEntityFactory } from '../../../../../cf-entity-factory'; import { organizationEntityType } from '../../../../../cf-entity-types'; -import { IOrgFavMetadata } from '../../../../../cf-metadata-types'; import { CF_ENDPOINT_TYPE } from '../../../../../cf-types'; import { CfUserService } from '../../../../../shared/data-services/cf-user.service'; import { @@ -89,7 +88,7 @@ export class CloudFoundryOrganizationBaseComponent { public extensionActions: StratosActionMetadata[] = getActionsFromExtensions(StratosActionType.CloudFoundryOrg); - public favorite$: Observable>; + public favorite$: Observable>; constructor( public cfEndpointService: CloudFoundryEndpointService, @@ -99,7 +98,7 @@ export class CloudFoundryOrganizationBaseComponent { this.schema = cfEntityFactory(organizationEntityType); this.favorite$ = cfOrgService.org$.pipe( first(), - map(org => getFavoriteFromEntity(org.entity, organizationEntityType, userFavoriteManager, CF_ENDPOINT_TYPE)) + map(org => getFavoriteFromEntity(org.entity, organizationEntityType, userFavoriteManager, CF_ENDPOINT_TYPE)) ); this.name$ = cfOrgService.org$.pipe( map(org => org.entity.entity.name), diff --git a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app/card/card-app.component.ts b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app/card/card-app.component.ts index ed54efa8b6..b7ad011740 100644 --- a/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app/card/card-app.component.ts +++ b/src/frontend/packages/cloud-foundry/src/shared/components/list/list-types/app/card/card-app.component.ts @@ -5,11 +5,10 @@ import { map, startWith } from 'rxjs/operators'; import { CFAppState } from '../../../../../../../../cloud-foundry/src/cf-app-state'; import { applicationEntityType } from '../../../../../../../../cloud-foundry/src/cf-entity-types'; -import { IAppFavMetadata } from '../../../../../../../../cloud-foundry/src/cf-metadata-types'; import { CardCell } from '../../../../../../../../core/src/shared/components/list/list.types'; import { APIResource } from '../../../../../../../../store/src/types/api.types'; import { ComponentEntityMonitorConfig, StratosStatus } from '../../../../../../../../store/src/types/shared.types'; -import { UserFavorite } from '../../../../../../../../store/src/types/user-favorites.types'; +import { IFavoriteMetadata, UserFavorite } from '../../../../../../../../store/src/types/user-favorites.types'; import { getFavoriteFromEntity } from '../../../../../../../../store/src/user-favorite-helpers'; import { UserFavoriteManager } from '../../../../../../../../store/src/user-favorite-manager'; import { IApp, ISpace } from '../../../../../../cf-api.types'; @@ -32,7 +31,7 @@ export class CardAppComponent extends CardCell> implements OnI entityConfig: ComponentEntityMonitorConfig; cfOrgSpace: CfOrgSpaceLabelService; - public favorite: UserFavorite; + public favorite: UserFavorite; constructor( private store: Store, diff --git a/src/frontend/packages/core/src/core/endpoints.service.ts b/src/frontend/packages/core/src/core/endpoints.service.ts index 6d61ef7af4..f7860ba3c3 100644 --- a/src/frontend/packages/core/src/core/endpoints.service.ts +++ b/src/frontend/packages/core/src/core/endpoints.service.ts @@ -7,10 +7,12 @@ import { first, map, skipWhile, withLatestFrom } from 'rxjs/operators'; import { RouterNav } from '../../../store/src/actions/router.actions'; import { EndpointOnlyAppState, IRequestEntityTypeState } from '../../../store/src/app-state'; import { entityCatalog } from '../../../store/src/entity-catalog/entity-catalog'; +import { EntityCatalogHelpers } from '../../../store/src/entity-catalog/entity-catalog.helper'; import { EndpointHealthCheck } from '../../../store/src/entity-catalog/entity-catalog.types'; import { AuthState } from '../../../store/src/reducers/auth.reducer'; import { endpointEntitiesSelector, endpointStatusSelector } from '../../../store/src/selectors/endpoint.selectors'; import { EndpointModel, EndpointState } from '../../../store/src/types/endpoint.types'; +import { IEndpointFavMetadata, UserFavorite } from '../../../store/src/types/user-favorites.types'; import { endpointHasMetricsByAvailable } from '../features/endpoints/endpoint-helpers'; import { EndpointHealthChecks } from './endpoints-health-checks'; import { UserService } from './user.service'; @@ -33,7 +35,14 @@ export class EndpointsService implements CanActivate { const catalogEntity = entityCatalog.getEndpoint(endpoint.cnsi_type, endpoint.sub_type); const metadata = catalogEntity.builders.entityBuilder.getMetadata(endpoint); if (catalogEntity) { - return catalogEntity.builders.entityBuilder.getLink(metadata); + const fav = new UserFavorite( + endpoint.guid, + endpoint.cnsi_type, + EntityCatalogHelpers.endpointType, + null, + metadata + ); + return fav.getLink(); } return ''; } diff --git a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts index 547b59827c..299100abb0 100644 --- a/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/favorites-meta-card/favorites-meta-card.component.ts @@ -56,7 +56,7 @@ export class FavoritesMetaCardComponent { } const entityDef = entityCatalog.getEntity(this.favorite.endpointType, this.favorite.entityType); const isValidObs = (entityDef.builders.entityBuilder && entityDef.builders.entityBuilder.getIsValid) ? - entityDef.builders.entityBuilder.getIsValid(this.favorite.metadata) : of(true); + entityDef.builders.entityBuilder.getIsValid(this.favorite) : of(true); isValidObs.pipe(first()).subscribe(isValid => { this.valid = isValid; if (!isValid) { diff --git a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts index a013b1805d..0ddf1499ca 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-types/endpoint/endpoint-card/endpoint-card.component.ts @@ -23,6 +23,7 @@ import { MenuItem } from '../../../../../../../../store/src/types/menu-item.type import { StratosStatus } from '../../../../../../../../store/src/types/shared.types'; import { UserFavoriteEndpoint } from '../../../../../../../../store/src/types/user-favorites.types'; import { UserFavoriteManager } from '../../../../../../../../store/src/user-favorite-manager'; +import { EndpointsService } from '../../../../../../core/endpoints.service'; import { safeUnsubscribe } from '../../../../../../core/utils.service'; import { coreEndpointListDetailsComponents } from '../../../../../../features/endpoints/endpoint-helpers'; import { createMetaCardMenuItemSeparator } from '../../../list-cards/meta-card/meta-card-base/meta-card.component'; @@ -76,9 +77,8 @@ export class EndpointCardComponent extends CardCell implements On this.address = getFullEndpointApiUrl(row); this.rowObs.next(row); if (this.endpointCatalogEntity) { - const metadata = this.endpointCatalogEntity.builders.entityBuilder.getMetadata(row); this.endpointLink = row.connectionStatus === 'connected' || this.endpointCatalogEntity.definition.unConnectable ? - this.endpointCatalogEntity.builders.entityBuilder.getLink(metadata) : null; + EndpointsService.getLinkForEndpoint(row) : null; this.connectionStatus = this.endpointCatalogEntity.definition.unConnectable ? 'connected' : row.connectionStatus; } this.updateInnerComponent(); diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index 89911f6ff4..ed0aa731e0 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -323,7 +323,7 @@ function generateNamespacesEntity(endpointDefinition: StratosEndpointExtensionDe definition, { actionBuilders: kubeNamespaceActionBuilders, entityBuilder: { - getIsValid: (favorite) => kubeEntityCatalog.namespace.api.get(favorite.name, favorite.kubeGuid).pipe(entityFetchedWithoutError()), + getIsValid: (fav) => kubeEntityCatalog.namespace.api.get(fav.metadata.name, fav.endpointId).pipe(entityFetchedWithoutError()), getMetadata: (namespace: any) => { return { endpointId: namespace.kubeGuid, @@ -332,7 +332,7 @@ function generateNamespacesEntity(endpointDefinition: StratosEndpointExtensionDe name: namespace.metadata.name, }; }, - getLink: metadata => `/kubernetes/${metadata.kubeGuid}/namespaces/${metadata.name}`, + getLink: favorite => `/kubernetes/${favorite.endpointId}/namespaces/${favorite.metadata.name}`, getGuid: namespace => namespace.metadata.uid, } }); diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts index 70e2b6a6da..0cd25e75eb 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts @@ -3,7 +3,6 @@ import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { filter, map } from 'rxjs/operators'; -import { IAppFavMetadata } from '../../../../cloud-foundry/src/cf-metadata-types'; import { IHeaderBreadcrumb } from '../../../../core/src/shared/components/page-header/page-header.types'; import { getFavoriteFromEntity } from '../../../../store/src/user-favorite-helpers'; import { UserFavoriteManager } from '../../../../store/src/user-favorite-manager'; @@ -13,6 +12,7 @@ import { KubernetesEndpointService } from '../services/kubernetes-endpoint.servi import { KubernetesNamespaceService } from '../services/kubernetes-namespace.service'; import { KubernetesAnalysisService } from '../services/kubernetes.analysis.service'; import { KubernetesService } from '../services/kubernetes.service'; +import { IFavoriteMetadata } from './../../../../store/src/types/user-favorites.types'; import { KUBERNETES_ENDPOINT_TYPE } from './../kubernetes-entity-factory'; @Component({ @@ -67,7 +67,7 @@ export class KubernetesNamespaceComponent { public favorite$ = this.kubeNamespaceService.namespace$.pipe( filter(app => !!app), - map(namespace => getFavoriteFromEntity( + map(namespace => getFavoriteFromEntity( { kubeGuid: this.kubeEndpointService.baseKube.guid, ...namespace, diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts index ae263dd3dc..7772977f19 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts @@ -1,7 +1,6 @@ import { ActionReducer } from '@ngrx/store'; import { IRequestEntityTypeState } from '../../app-state'; -import { getFullEndpointApiUrl } from '../../endpoint-utils'; import { EntitiesFetchHandler, EntitiesInfoHandler, @@ -17,7 +16,7 @@ import { EntitySchema } from '../../helpers/entity-schema'; import { endpointEntityType, STRATOS_ENDPOINT_TYPE, stratosEntityFactory } from '../../helpers/stratos-entity-factory'; import { EndpointModel } from '../../types/endpoint.types'; import { APISuccessOrFailedAction, EntityRequestAction } from '../../types/request.types'; -import { IEndpointFavMetadata } from '../../types/user-favorites.types'; +import { IEndpointFavMetadata, UserFavorite } from '../../types/user-favorites.types'; import { ActionBuilderAction, ActionOrchestrator, @@ -319,11 +318,7 @@ export class StratosCatalogEndpointEntity extends StratosBaseCatalogEntity = { getMetadata: endpoint => ({ name: endpoint.name, - guid: endpoint.guid, - address: getFullEndpointApiUrl(endpoint), - user: endpoint.user ? endpoint.user.name : undefined, subType: endpoint.sub_type, - admin: endpoint.user ? endpoint.user.admin ? 'Yes' : 'No' : undefined }), getLink: () => null, getGuid: metadata => metadata.guid, @@ -332,7 +327,7 @@ export class StratosCatalogEndpointEntity extends StratosBaseCatalogEntity; constructor( entity: StratosEndpointExtensionDefinition | IStratosEndpointDefinition, - getLink?: (metadata: IEndpointFavMetadata) => string + getLink?: (entity: UserFavorite) => string ) { const fullEntity: IStratosEndpointDefinition = { ...entity, diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts index 9376fc7a8a..8c37860ffe 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog.types.ts @@ -232,13 +232,13 @@ export interface IStratosEntityBuilder { getMetadata(entity: Y): T; // TODO This should be used in the entities schema. getGuid(entity: Y): string; - getLink?(entityMetadata: T): string; + getLink?(favorite: UserFavorite): string; getSubTypeLabels?(entityMetadata: T): { singular: string, plural: string, }; // Is the underlying entity for the favorite valid? - getIsValid?(entityMetadata): Observable; + getIsValid?(favorite: UserFavorite): Observable; /** * Actions that don't effect an individual entity i.e. create new * @returns global actions diff --git a/src/frontend/packages/store/src/types/user-favorites.types.ts b/src/frontend/packages/store/src/types/user-favorites.types.ts index 07ece3e6f2..fb79ace44b 100644 --- a/src/frontend/packages/store/src/types/user-favorites.types.ts +++ b/src/frontend/packages/store/src/types/user-favorites.types.ts @@ -23,10 +23,6 @@ export interface IFavoriteMetadata { [key: string]: string; } export interface IEndpointFavMetadata extends IFavoriteMetadata { - guid: string; - address: string; - user: string; - admin: string; subType: string; } @@ -95,7 +91,7 @@ export class UserFavorite implement // Get the link to navigate to the view for the given entity backing this user favorite public getLink(): string { - return this.entityBuilder.getLink ? this.entityBuilder.getLink(this.metadata) : null; + return this.entityBuilder.getLink ? this.entityBuilder.getLink(this) : null; } // Get the type name, e.g. 'Application' From fef76be9c4dbd25d0c5f69448cc52473e381875b Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 24 Nov 2020 08:43:56 +0000 Subject: [PATCH 69/74] Fix merge issue --- .../packages/kubernetes/src/helm/helm-entity-generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/packages/kubernetes/src/helm/helm-entity-generator.ts b/src/frontend/packages/kubernetes/src/helm/helm-entity-generator.ts index c6c6d52c08..44c7535cd5 100644 --- a/src/frontend/packages/kubernetes/src/helm/helm-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/helm/helm-entity-generator.ts @@ -107,7 +107,7 @@ export function generateHelmEntities(): StratosBaseCatalogEntity[] { function generateEndpointEntity(endpointDefinition: StratosEndpointExtensionDefinition) { helmEntityCatalog.endpoint = new StratosCatalogEndpointEntity( endpointDefinition, - (metadata) => `/monocular/charts/${metadata.name}`, + (fav) => `/monocular/charts/${fav.metadata.name}`, ); return helmEntityCatalog.endpoint; } From 6c5b0f40e94c7bb742df9e85e30043a4b6272390 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 24 Nov 2020 10:20:37 +0000 Subject: [PATCH 70/74] Remove unrelated change --- .../autoscaler-tab-extension.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.ts b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.ts index 9580b081ba..a71a1e76e1 100644 --- a/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.ts +++ b/src/frontend/packages/cf-autoscaler/src/features/autoscaler-tab-extension/autoscaler-tab-extension.component.ts @@ -77,8 +77,8 @@ import { appAutoscalerAppMetricEntityType, autoscalerEntityFactory } from '../.. switchMap(app => cups.can( CfCurrentUserPermissions.APPLICATION_EDIT, endpointGuid, - app.entity.entity.space?.entity.organization_guid, - app.entity.entity.space?.metadata.guid + app.entity.entity.space.entity.organization_guid, + app.entity.entity.space.metadata.guid )), ); From 3c5c0a862fc8e78c6f103f6315101fe4696a9d72 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 24 Nov 2020 13:39:10 +0000 Subject: [PATCH 71/74] Address PR feedback --- .../home-page-endpoint-card.component.ts | 1 - .../page-header/page-header.component.ts | 1 + .../kubernetes/kubernetes-entity-catalog.ts | 3 ++- .../services/kubernetes-endpoint.service.ts | 23 ++++++----------- .../entity-catalog-entity.ts | 5 +++- .../user-favorites-groups.reducer.spec.ts | 10 ++++---- .../user-favorites-groups.reducer.ts | 25 ++++++++++--------- .../selectors/favorite-groups.selectors.ts | 4 +-- .../store/src/types/user-favorites.types.ts | 18 ++++++++++--- .../store/src/user-favorite-helpers.ts | 18 ++++++------- .../store/src/user-favorite-manager.ts | 2 +- 11 files changed, 56 insertions(+), 54 deletions(-) diff --git a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts index 8c54c4b6e7..da60cf4a40 100644 --- a/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts +++ b/src/frontend/packages/core/src/features/home/home/home-page-endpoint-card/home-page-endpoint-card.component.ts @@ -119,7 +119,6 @@ export class HomePageEndpointCardComponent implements OnInit, OnDestroy, AfterVi if (endpointEntity && endpointEntity.definition.homeCard && endpointEntity.definition.homeCard.component) { this.createCard(endpointEntity); } else { - console.warn(`No endpoint home card for ${this.endpoint.guid}`); this.createCard(undefined); } } diff --git a/src/frontend/packages/core/src/shared/components/page-header/page-header.component.ts b/src/frontend/packages/core/src/shared/components/page-header/page-header.component.ts index c713a7d569..f845f5b6c1 100644 --- a/src/frontend/packages/core/src/shared/components/page-header/page-header.component.ts +++ b/src/frontend/packages/core/src/shared/components/page-header/page-header.component.ts @@ -99,6 +99,7 @@ export class PageHeaderComponent implements OnDestroy, AfterViewInit { routerLink: favorite.getLink(), prettyType: favorite.getPrettyTypeName(), endpointId: favorite.endpointId, + metadata: { name: favorite.metadata.name }, })); } } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-catalog.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-catalog.ts index f90866eebe..6836c0bcda 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-catalog.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-catalog.ts @@ -22,6 +22,7 @@ import { KubernetesStatefulSet, KubeService, } from './store/kube.types'; +import { KubeDashboardStatus } from './store/kubernetes.effects'; /** * A strongly typed collection of Kube Catalog Entities. @@ -35,7 +36,7 @@ export class KubeEntityCatalog { public node: StratosCatalogEntity; public namespace: StratosCatalogEntity; public service: StratosCatalogEntity; - public dashboard: StratosCatalogEntity; + public dashboard: StratosCatalogEntity; public analysisReport: StratosCatalogEntity; } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-endpoint.service.ts b/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-endpoint.service.ts index 3195fd4898..8ca622e099 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-endpoint.service.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes-endpoint.service.ts @@ -72,7 +72,7 @@ export class KubernetesEndpointService { ); } - public static kubeDashboardConfigured(store: Store, kubeGuid: string): Observable { + public static getKubeDashboardStatus(store: Store, kubeGuid: string): Observable { const kubeDashboardEnabled$ = store.select('auth').pipe( filter(auth => !!auth.sessionData['plugin-config']), map(auth => auth.sessionData['plugin-config'].kubeDashboardEnabled === 'true') @@ -83,8 +83,11 @@ export class KubernetesEndpointService { filter(status => !!status) ); - return kubeDashboardEnabled$.pipe( - switchMap(enabled => enabled ? kubeDashboardStatus$ : of(null)), + return kubeDashboardEnabled$.pipe(switchMap(enabled => enabled ? kubeDashboardStatus$ : of(null))); + } + + public static kubeDashboardConfigured(store: Store, kubeGuid: string): Observable { + return KubernetesEndpointService.getKubeDashboardStatus(store, kubeGuid).pipe( map(status => status && status.installed && !!status.serviceAccount && !!status.service), ); } @@ -266,18 +269,8 @@ export class KubernetesEndpointService { map(auth => auth.sessionData['plugin-config'].kubeTerminalEnabled === 'true') ); - const kubeDashboardStatus$ = kubeEntityCatalog.dashboard.store.getEntityService(this.kubeGuid).waitForEntity$.pipe( - map(status => status.entity), - filter(status => !!status) - ); - - this.kubeDashboardStatus$ = this.kubeDashboardEnabled$.pipe( - switchMap(enabled => enabled ? kubeDashboardStatus$ : of(null)), - ); - - this.kubeDashboardConfigured$ = this.kubeDashboardStatus$.pipe( - map(status => status && status.installed && !!status.serviceAccount && !!status.service), - ); + this.kubeDashboardStatus$ = KubernetesEndpointService.getKubeDashboardStatus(this.store, this.kubeGuid); + this.kubeDashboardConfigured$ = KubernetesEndpointService.kubeDashboardConfigured(this.store, this.kubeGuid); this.kubeDashboardLabel$ = this.kubeDashboardStatus$.pipe( map(status => { diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts index 7772977f19..f4c7fa3630 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts @@ -33,6 +33,9 @@ import { IStratosEntityDefinition, StratosEndpointExtensionDefinition, } from '../entity-catalog.types'; +import { + FavoritesSidePanelComponent, +} from './../../../../core/src/features/home/home/favorites-side-panel/favorites-side-panel.component'; import { ActionBuilderConfigMapper } from './action-builder-config.mapper'; import { ActionDispatchers, EntityCatalogEntityStoreHelpers } from './entity-catalog-entity-store-helpers'; import { EntityCatalogEntityStore } from './entity-catalog-entity.types'; @@ -327,7 +330,7 @@ export class StratosCatalogEndpointEntity extends StratosBaseCatalogEntity; constructor( entity: StratosEndpointExtensionDefinition | IStratosEndpointDefinition, - getLink?: (entity: UserFavorite) => string + getLink?: (FavoritesSidePanelComponent: UserFavorite) => string ) { const fullEntity: IStratosEndpointDefinition = { ...entity, diff --git a/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.spec.ts b/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.spec.ts index ad3370c52f..55bed2bc32 100644 --- a/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.spec.ts +++ b/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.spec.ts @@ -7,7 +7,7 @@ import { } from '../../actions/user-favourites.actions'; import { getDefaultFavoriteGroupsState, IUserFavoritesGroupsState } from '../../types/favorite-groups.types'; import { IEndpointFavMetadata, UserFavorite } from '../../types/user-favorites.types'; -import { deriveEndpointFavoriteFromFavorite } from '../../user-favorite-helpers'; +import { getEndpointIDFromFavorite } from '../../user-favorite-helpers'; import { userFavoriteGroupsReducer } from './user-favorites-groups.reducer'; const endpointFavorite = () => new UserFavorite( @@ -70,14 +70,14 @@ describe('userFavoritesReducer', () => { it(' [empty state] should add new entity and mark endpoint group as ethereal', () => { const fav = favorite(); - const endpointFav = deriveEndpointFavoriteFromFavorite(fav); + const endpointFavGuid = getEndpointIDFromFavorite(fav); const action = new SaveUserFavoriteSuccessAction(fav); const newState = userFavoriteGroupsReducer(undefined, action); const defaultState = getDefaultFavoriteGroupsState(); expect(newState).toEqual({ ...defaultState, groups: { - [endpointFav.guid]: { + [endpointFavGuid]: { endpoint: {}, ethereal: true, entitiesIds: [ @@ -141,7 +141,7 @@ describe('userFavoritesReducer', () => { const action = new GetUserFavoritesSuccessAction(favs); const newState = userFavoriteGroupsReducer(undefined, action); - const endpoint3Fav = deriveEndpointFavoriteFromFavorite(fav3); + const endpoint3FavGuid = getEndpointIDFromFavorite(fav3); const defaultState = getDefaultFavoriteGroupsState(); expect(newState).toEqual({ ...defaultState, @@ -161,7 +161,7 @@ describe('userFavoritesReducer', () => { fav21.guid ] }, - [endpoint3Fav.guid]: { + [endpoint3FavGuid]: { endpoint: {}, ethereal: true, entitiesIds: [ diff --git a/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts b/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts index 3b99042c6d..112a22f58a 100644 --- a/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts +++ b/src/frontend/packages/store/src/reducers/current-user-roles-reducer/user-favorites-groups.reducer.ts @@ -15,7 +15,7 @@ import { IUserFavoritesGroupsState, } from '../../types/favorite-groups.types'; import { IFavoriteMetadata, UserFavorite } from '../../types/user-favorites.types'; -import { deriveEndpointFavoriteFromFavorite, isEndpointTypeFavorite } from '../../user-favorite-helpers'; +import { getEndpointIDFromFavorite } from '../../user-favorite-helpers'; export function userFavoriteGroupsReducer( state: IUserFavoritesGroupsState = getDefaultFavoriteGroupsState(), @@ -61,25 +61,26 @@ export function userFavoriteGroupsReducer( function buildFavoritesGroups(action: GetUserFavoritesSuccessAction) { const { favorites } = action; return favorites.reduce((favoriteGroups, favorite) => { - const userFavorite = deriveEndpointFavoriteFromFavorite(favorite); - favoriteGroups[userFavorite.guid] = addFavoriteToGroup(favoriteGroups[userFavorite.guid], favorite); + const endpointGuid = getEndpointIDFromFavorite(favorite); + favoriteGroups[endpointGuid] = addFavoriteToGroup(favoriteGroups[endpointGuid], favorite); return favoriteGroups; }, {} as IUserFavoritesGroups); } function removeFavoriteFromGroup(state: IUserFavoritesGroups, action: RemoveUserFavoriteSuccessAction): IUserFavoritesGroups { const { favorite } = action; - const endpointFavorite = deriveEndpointFavoriteFromFavorite(favorite); - const userGroup = state[endpointFavorite.guid] || getDefaultFavoriteGroup(); - if (isEndpointTypeFavorite(favorite)) { + const endpointGuid = getEndpointIDFromFavorite(favorite); + const userGroup = state[endpointGuid] || getDefaultFavoriteGroup(); + // Favorite will not have and entityId if it is for an endpoint + if (!favorite.entityId) { if (!groupHasEntities(userGroup)) { - return removeGroup(state, endpointFavorite.guid); + return removeGroup(state, endpointGuid); } // The endpoint has been removed but dependant entities are still within the group // The group is now ethereal return { ...state, - [endpointFavorite.guid]: { + [endpointGuid]: { ...userGroup, ethereal: true } @@ -87,11 +88,11 @@ function removeFavoriteFromGroup(state: IUserFavoritesGroups, action: RemoveUser } else { const entitiesIds = userGroup.entitiesIds.filter(id => id !== favorite.guid); if (!entitiesIds.length && userGroup.ethereal) { - return removeGroup(state, endpointFavorite.guid); + return removeGroup(state, endpointGuid); } return { ...state, - [endpointFavorite.guid]: { + [endpointGuid]: { ...userGroup, entitiesIds } @@ -113,7 +114,7 @@ function groupHasEntities(group: IUserFavoriteGroup) { function addEntityFavorite(favoriteGroups: IUserFavoritesGroups, action: SaveUserFavoriteSuccessAction): IUserFavoritesGroups { const { favorite } = action; - const { guid } = deriveEndpointFavoriteFromFavorite(favorite); + const guid = getEndpointIDFromFavorite(favorite); const group = favoriteGroups[guid]; const newGroup = addFavoriteToGroup(group, favorite); return { @@ -131,7 +132,7 @@ function addFavoriteToGroup(favoriteGroup: IUserFavoriteGroup = getDefaultFavori }; const { guid } = favorite; - const isEndpoint = isEndpointTypeFavorite(favorite); + const isEndpoint = !favorite.entityId; if (isEndpoint) { fg.endpoint = favorite; } diff --git a/src/frontend/packages/store/src/selectors/favorite-groups.selectors.ts b/src/frontend/packages/store/src/selectors/favorite-groups.selectors.ts index 12139d6f78..f60988e8b0 100644 --- a/src/frontend/packages/store/src/selectors/favorite-groups.selectors.ts +++ b/src/frontend/packages/store/src/selectors/favorite-groups.selectors.ts @@ -5,7 +5,6 @@ import { entityCatalog } from '../entity-catalog/entity-catalog'; import { STRATOS_ENDPOINT_TYPE, userFavouritesEntityType } from '../helpers/stratos-entity-factory'; import { IUserFavoriteGroup, IUserFavoritesGroups, IUserFavoritesGroupsState } from '../types/favorite-groups.types'; import { IFavoriteMetadata, UserFavorite } from '../types/user-favorites.types'; -import { deriveEndpointFavoriteFromFavorite } from '../user-favorite-helpers'; const favoritesEntityKey = entityCatalog.getEntityKey(STRATOS_ENDPOINT_TYPE, userFavouritesEntityType); @@ -25,9 +24,8 @@ export const favoriteGroupsSelector = compose( ); const favoriteGroupSelector = (favorite: UserFavorite) => { - const endpointFavorite = deriveEndpointFavoriteFromFavorite(favorite); return (groups: IUserFavoritesGroups): IUserFavoriteGroup => { - return groups[endpointFavorite.guid]; + return groups[favorite.endpointId]; }; }; diff --git a/src/frontend/packages/store/src/types/user-favorites.types.ts b/src/frontend/packages/store/src/types/user-favorites.types.ts index fb79ace44b..cf0c2178dc 100644 --- a/src/frontend/packages/store/src/types/user-favorites.types.ts +++ b/src/frontend/packages/store/src/types/user-favorites.types.ts @@ -13,9 +13,12 @@ export interface IFavoritesInfo { /** * A user favorite blueprint. Can be used to fetch the full entity from a particular endpoint. */ -export interface IFavoriteTypeInfo { +export interface IFavoriteTypeInfo { endpointType: string; entityType: string; + entityId: string; + endpointId: string; + metadata: T; } export interface IFavoriteMetadata { @@ -43,12 +46,15 @@ export interface FavoriteIconData { const favoriteGuidSeparator = '-'; -export class UserFavorite implements IFavoriteTypeInfo { +export class UserFavorite implements IFavoriteTypeInfo { public guid: string; private catalogEntity: StratosBaseCatalogEntity; private entityBuilder: IStratosEntityBuilder; + public entityId: string; + public metadata: T; + constructor( public endpointId: string, public endpointType: string, @@ -56,9 +62,13 @@ export class UserFavorite implement entityType should correspond to a type in the requestData part of the store. */ public entityType: string, - public entityId?: string, - public metadata: T = null + entityId?: string, + metadata?: T ) { + // Make sure these default to undefined + this.entityId = entityId; + this.metadata = metadata; + // Set the guid for this favorite this.buildFavoriteStoreEntityGuid(); this.catalogEntity = entityCatalog.getEntity(this.endpointType, this.entityType); diff --git a/src/frontend/packages/store/src/user-favorite-helpers.ts b/src/frontend/packages/store/src/user-favorite-helpers.ts index 768c671e79..64af932787 100644 --- a/src/frontend/packages/store/src/user-favorite-helpers.ts +++ b/src/frontend/packages/store/src/user-favorite-helpers.ts @@ -1,13 +1,10 @@ import { entityCatalog } from './entity-catalog/entity-catalog'; import { IEntityMetadata } from './entity-catalog/entity-catalog.types'; +import { endpointEntityType } from './helpers/stratos-entity-factory'; import { IFavoriteMetadata, UserFavorite } from './types/user-favorites.types'; import { UserFavoriteManager } from './user-favorite-manager'; -export function isEndpointTypeFavorite(favorite: UserFavorite) { - return !favorite.entityId; -} - -// Uses the endpoint definition to get the helper that can look up an entitty +// Uses the endpoint definition to get the helper that can look up an entity export function getFavoriteFromEntity( entity, entityType: string, @@ -23,11 +20,10 @@ export function getFavoriteFromEntity) { - if (favorite.entityType !== 'endpoint') { - return new UserFavorite( - favorite.endpointId, favorite.endpointType, 'endpoint', null, favorite.metadata - ); + +export function getEndpointIDFromFavorite(favorite: UserFavorite): string { + if (favorite.entityType !== endpointEntityType) { + return new UserFavorite(favorite.endpointId, favorite.endpointType, endpointEntityType, null, {name: ''}).guid; } - return favorite; + return favorite.guid; } diff --git a/src/frontend/packages/store/src/user-favorite-manager.ts b/src/frontend/packages/store/src/user-favorite-manager.ts index 6609e6b9c1..f20f949854 100644 --- a/src/frontend/packages/store/src/user-favorite-manager.ts +++ b/src/frontend/packages/store/src/user-favorite-manager.ts @@ -112,7 +112,7 @@ export class UserFavoriteManager { }); } - public getUserFavoriteFromObject = (f: UserFavorite): UserFavorite => { + public getUserFavoriteFromObject = (f: IFavoriteTypeInfo): UserFavorite => { return new UserFavorite(f.endpointId, f.endpointType, f.entityType, f.entityId, f.metadata); } From 7cb342f87ffc1a29e2b9834bf1071124e678a0da Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 24 Nov 2020 14:17:30 +0000 Subject: [PATCH 72/74] Bug fixes --- .../store/src/effects/user-favorites-effect.ts | 2 +- .../src/selectors/favorite-groups.selectors.ts | 4 +++- .../store/src/types/user-favorites.types.ts | 13 +++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/frontend/packages/store/src/effects/user-favorites-effect.ts b/src/frontend/packages/store/src/effects/user-favorites-effect.ts index 24bb7050e3..26165f8617 100644 --- a/src/frontend/packages/store/src/effects/user-favorites-effect.ts +++ b/src/frontend/packages/store/src/effects/user-favorites-effect.ts @@ -45,7 +45,7 @@ export class UserFavoritesEffect { mergeMap(action => { const actionType = 'update'; this.store.dispatch(new StartRequestAction(action, actionType)); - return this.http.post>(favoriteUrlPath, action.favorite).pipe( + return this.http.post>(favoriteUrlPath, action.favorite.getPayload()).pipe( switchMap(newFavorite => { this.store.dispatch(new WrapperRequestActionSuccess(null, action, actionType)); this.store.dispatch(new SaveUserFavoriteSuccessAction(newFavorite)); diff --git a/src/frontend/packages/store/src/selectors/favorite-groups.selectors.ts b/src/frontend/packages/store/src/selectors/favorite-groups.selectors.ts index f60988e8b0..dbfa0879f2 100644 --- a/src/frontend/packages/store/src/selectors/favorite-groups.selectors.ts +++ b/src/frontend/packages/store/src/selectors/favorite-groups.selectors.ts @@ -5,6 +5,7 @@ import { entityCatalog } from '../entity-catalog/entity-catalog'; import { STRATOS_ENDPOINT_TYPE, userFavouritesEntityType } from '../helpers/stratos-entity-factory'; import { IUserFavoriteGroup, IUserFavoritesGroups, IUserFavoritesGroupsState } from '../types/favorite-groups.types'; import { IFavoriteMetadata, UserFavorite } from '../types/user-favorites.types'; +import { getEndpointIDFromFavorite } from '../user-favorite-helpers'; const favoritesEntityKey = entityCatalog.getEntityKey(STRATOS_ENDPOINT_TYPE, userFavouritesEntityType); @@ -24,8 +25,9 @@ export const favoriteGroupsSelector = compose( ); const favoriteGroupSelector = (favorite: UserFavorite) => { + const endpointFavoriteGuid = getEndpointIDFromFavorite(favorite); return (groups: IUserFavoritesGroups): IUserFavoriteGroup => { - return groups[favorite.endpointId]; + return groups[endpointFavoriteGuid]; }; }; diff --git a/src/frontend/packages/store/src/types/user-favorites.types.ts b/src/frontend/packages/store/src/types/user-favorites.types.ts index cf0c2178dc..dbbb9b63a4 100644 --- a/src/frontend/packages/store/src/types/user-favorites.types.ts +++ b/src/frontend/packages/store/src/types/user-favorites.types.ts @@ -14,6 +14,7 @@ export interface IFavoritesInfo { * A user favorite blueprint. Can be used to fetch the full entity from a particular endpoint. */ export interface IFavoriteTypeInfo { + guid: string; endpointType: string; entityType: string; entityId: string; @@ -94,6 +95,18 @@ export class UserFavorite implement } } + // Get payload for this entity that can be sent to the backend + public getPayload(): IFavoriteTypeInfo { + return { + endpointId: this.endpointId, + endpointType: this.endpointType, + entityType: this.entityType, + entityId: this.entityId, + guid: this.guid, + metadata: this.metadata + }; + } + public canFavorite(): boolean { // What do we need to be able to favorite an entity? return !!this.entityBuilder.getGuid && !!this.entityBuilder.getMetadata && !!this.entityBuilder.getLink; From ec1d114d46fa083b1468115d4639e18f95a5005a Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Wed, 25 Nov 2020 09:38:28 +0000 Subject: [PATCH 73/74] Address PR feedback --- .../table-cell-favorite.component.ts | 4 +- ...bernetes-namespaces-list-config.service.ts | 42 ++++++------------- .../entity-catalog-entity.ts | 5 +-- 3 files changed, 15 insertions(+), 36 deletions(-) diff --git a/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component.ts b/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component.ts index 0ddad50b8b..c4b5bdfbec 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component.ts @@ -36,8 +36,8 @@ export class TableCellFavoriteComponent extends } private createUserFavorite() { - if (this.row && this.config) { - this.favorite = this.config.createUserFavorite(this.row); + if (this.pRow && this.pConfig) { + this.favorite = this.pConfig.createUserFavorite(this.pRow); this.canFavorite = this.favorite.canFavorite(); } } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/list-types/kubernetes-namespaces/kubernetes-namespaces-list-config.service.ts b/src/frontend/packages/kubernetes/src/kubernetes/list-types/kubernetes-namespaces/kubernetes-namespaces-list-config.service.ts index 587fcf631d..016f636f75 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/list-types/kubernetes-namespaces/kubernetes-namespaces-list-config.service.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/list-types/kubernetes-namespaces/kubernetes-namespaces-list-config.service.ts @@ -1,16 +1,19 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; -import { Observable } from 'rxjs'; -import { filter, first, map, tap } from 'rxjs/operators'; +import { filter } from 'rxjs/operators'; +import { + createTableColumnFavorite, +} from '../../../../../core/src/shared/components/list/list-table/table-cell-favorite/table-cell-favorite.component'; import { ITableColumn } from '../../../../../core/src/shared/components/list/list-table/table.types'; import { IListConfig, ListViewTypes } from '../../../../../core/src/shared/components/list/list.component.types'; import { AppState } from '../../../../../store/src/public-api'; +import { IFavoriteMetadata, UserFavorite } from '../../../../../store/src/types/user-favorites.types'; import { BaseKubeGuid } from '../../kubernetes-page.types'; -import { KubernetesEndpointService } from '../../services/kubernetes-endpoint.service'; import { KubernetesNamespace } from '../../store/kube.types'; import { defaultHelmKubeListPageSize } from '../kube-helm-list-types'; import { createKubeAgeColumn } from '../kube-list.helper'; +import { KUBERNETES_ENDPOINT_TYPE, kubernetesNamespacesEntityType } from './../../kubernetes-entity-factory'; import { KubeNamespacePodCountComponent } from './kube-namespace-pod-count/kube-namespace-pod-count.component'; import { KubernetesNamespaceLinkComponent } from './kubernetes-namespace-link/kubernetes-namespace-link.component'; import { KubernetesNamespacesDataSource } from './kubernetes-namespaces-data-source'; @@ -31,17 +34,6 @@ export class KubernetesNamespacesListConfigService implements IListConfig 'Dashboard', - // cellDefinition: { - // getValue: () => 'View', - // getLink: (row: KubernetesNamespace) => { - // return `/kubernetes/${this.kubeId.guid}/dashboard/overview?namespace=${row.metadata.name}`; - // }, - // }, - // cellFlex: '3', - // }, { columnId: 'pods', headerCell: () => 'Pods', cellComponent: KubeNamespacePodCountComponent, @@ -59,7 +51,12 @@ export class KubernetesNamespacesListConfigService implements IListConfig => { + return new UserFavorite(row.metadata.kubeId, KUBERNETES_ENDPOINT_TYPE, kubernetesNamespacesEntityType, row.metadata.name, + {name: row.metadata.name} + ); + }), ]; pageSizeOptions = defaultHelmKubeListPageSize; @@ -69,7 +66,6 @@ export class KubernetesNamespacesListConfigService implements IListConfig; getGlobalActions = () => null; getMultiActions = () => []; @@ -77,26 +73,12 @@ export class KubernetesNamespacesListConfigService implements IListConfig this.columns; getDataSource = () => this.podsDataSource; getMultiFiltersConfigs = () => []; - getInitialised = () => this.initialised$; constructor( store: Store, private kubeId: BaseKubeGuid, - kubeService: KubernetesEndpointService ) { this.podsDataSource = new KubernetesNamespacesDataSource(store, this.kubeId, this); - - const hasDashboard = kubeService.kubeDashboardConfigured$.pipe( - first(), - tap((enabled) => { - if (!enabled) { - this.columns = this.columns.filter(column => column.columnId !== 'view'); - } - }) - ); - this.initialised$ = hasDashboard.pipe( - map(() => true) - ); } } diff --git a/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts b/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts index f4c7fa3630..516dc0b6ce 100644 --- a/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts +++ b/src/frontend/packages/store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity.ts @@ -33,9 +33,6 @@ import { IStratosEntityDefinition, StratosEndpointExtensionDefinition, } from '../entity-catalog.types'; -import { - FavoritesSidePanelComponent, -} from './../../../../core/src/features/home/home/favorites-side-panel/favorites-side-panel.component'; import { ActionBuilderConfigMapper } from './action-builder-config.mapper'; import { ActionDispatchers, EntityCatalogEntityStoreHelpers } from './entity-catalog-entity-store-helpers'; import { EntityCatalogEntityStore } from './entity-catalog-entity.types'; @@ -330,7 +327,7 @@ export class StratosCatalogEndpointEntity extends StratosBaseCatalogEntity; constructor( entity: StratosEndpointExtensionDefinition | IStratosEndpointDefinition, - getLink?: (FavoritesSidePanelComponent: UserFavorite) => string + getLink?: (favorite: UserFavorite) => string ) { const fullEntity: IStratosEndpointDefinition = { ...entity, From d9d1e383a930fb0e9e23818d83f7b81c854f846e Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Wed, 25 Nov 2020 11:02:36 +0000 Subject: [PATCH 74/74] Fix link for kubernetes endpoints --- .../kubernetes/src/kubernetes/kubernetes-entity-generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts index ed0aa731e0..cd77a719e1 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -255,7 +255,7 @@ export function generateKubernetesEntities(): StratosBaseCatalogEntity[] { function generateEndpointEntity(endpointDefinition: StratosEndpointExtensionDefinition) { kubeEntityCatalog.endpoint = new StratosCatalogEndpointEntity( endpointDefinition, - metadata => `/kubernetes/${metadata.guid}` + favorite => `/kubernetes/${favorite.endpointId}` ); return kubeEntityCatalog.endpoint; }