diff --git a/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.html b/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.html index 4e8ff0b76e..2c5ff464c0 100644 --- a/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.html +++ b/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.html @@ -1,4 +1,5 @@ -
+
star star_border
\ No newline at end of file diff --git a/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.scss b/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.scss index 21c3ebf586..c27f56975a 100644 --- a/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.scss +++ b/src/frontend/packages/core/src/core/entity-favorite-star/entity-favorite-star.component.scss @@ -1,8 +1,19 @@ $size: 24px; +$small: 20px; .favorite-star { align-items: center; cursor: pointer; display: flex; height: $size; width: $size; + &__small { + height: $small; + width: $small; + mat-icon { + font-size: $small; + height: $small; + line-height: $small; + width: $small; + } + } } 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 68bf8793ac..e01afdbfa2 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 @@ -27,6 +27,8 @@ export class EntityFavoriteStarComponent { @Input() public confirmRemoval = false; + @Input() small = false; + public isFavorite$: Observable; private confirmationDialogConfig = new ConfirmationDialogConfig('Unfavorite?', '', 'Yes', true); diff --git a/src/frontend/packages/core/src/shared/components/json-viewer/json-viewer.component.html b/src/frontend/packages/core/src/shared/components/json-viewer/json-viewer.component.html index fbc96182c2..15028ba2a9 100644 --- a/src/frontend/packages/core/src/shared/components/json-viewer/json-viewer.component.html +++ b/src/frontend/packages/core/src/shared/components/json-viewer/json-viewer.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/frontend/packages/core/src/shared/components/json-viewer/json-viewer.component.scss b/src/frontend/packages/core/src/shared/components/json-viewer/json-viewer.component.scss index e6d507bf40..c55cc8f331 100644 --- a/src/frontend/packages/core/src/shared/components/json-viewer/json-viewer.component.scss +++ b/src/frontend/packages/core/src/shared/components/json-viewer/json-viewer.component.scss @@ -98,4 +98,17 @@ $type-colors: ( .expandable > .toggler { cursor: pointer; } -} \ No newline at end of file +} + +// Smaller text +.ngx-json-viewer.small-text { + font-size: 12px; + + * { + font-size: 12px; + } + + .segment .children { + margin-left: 0; + } +} diff --git a/src/frontend/packages/core/src/shared/components/json-viewer/json-viewer.component.ts b/src/frontend/packages/core/src/shared/components/json-viewer/json-viewer.component.ts index 6324d0677a..f6e33a22f3 100644 --- a/src/frontend/packages/core/src/shared/components/json-viewer/json-viewer.component.ts +++ b/src/frontend/packages/core/src/shared/components/json-viewer/json-viewer.component.ts @@ -26,6 +26,8 @@ export class JsonViewerComponent implements OnChanges { */ @Input() cleanOnChange = true; + @Input() small = false; + segments: Segment[] = []; expand() { diff --git a/src/frontend/packages/core/src/shared/components/list/list-generics/helpers/action-or-config-helpers.ts b/src/frontend/packages/core/src/shared/components/list/list-generics/helpers/action-or-config-helpers.ts index 01e95c6333..35f63253c0 100644 --- a/src/frontend/packages/core/src/shared/components/list/list-generics/helpers/action-or-config-helpers.ts +++ b/src/frontend/packages/core/src/shared/components/list/list-generics/helpers/action-or-config-helpers.ts @@ -87,12 +87,15 @@ export class ListActionOrConfigHelpers { dsOverrides?: Partial> ): IListDataSourceConfig { const { action, catalogEntity } = ListActionOrConfigHelpers.createListAction(actionOrConfig); + const schema = catalogEntity.getSchema(action.schemaKey); return { store, action, paginationKey: action.paginationKey, schema: catalogEntity.getSchema(action.schemaKey), - getRowUniqueId: entity => catalogEntity.getGuidFromEntity(entity), + getRowUniqueId: entity => { + return catalogEntity.getGuidFromEntity(entity) || schema.getId(entity); + }, listConfig, isLocal: true, // assume true unless overwritten ...dsOverrides diff --git a/src/frontend/packages/core/src/shared/components/sidepanel-preview/sidepanel-preview.component.html b/src/frontend/packages/core/src/shared/components/sidepanel-preview/sidepanel-preview.component.html index 3b14162902..55c959b517 100644 --- a/src/frontend/packages/core/src/shared/components/sidepanel-preview/sidepanel-preview.component.html +++ b/src/frontend/packages/core/src/shared/components/sidepanel-preview/sidepanel-preview.component.html @@ -1,6 +1,10 @@

{{ title }}

+ +
+ +
diff --git a/src/frontend/packages/core/src/shared/components/sidepanel-preview/sidepanel-preview.component.scss b/src/frontend/packages/core/src/shared/components/sidepanel-preview/sidepanel-preview.component.scss index a560f4bd10..440dc848e7 100644 --- a/src/frontend/packages/core/src/shared/components/sidepanel-preview/sidepanel-preview.component.scss +++ b/src/frontend/packages/core/src/shared/components/sidepanel-preview/sidepanel-preview.component.scss @@ -37,6 +37,11 @@ top: 7px; } } + &__custom { + align-self: center; + flex: 1; + margin: 0 50px 0 10px; + } &__content { flex: 1; @@ -50,4 +55,11 @@ right: 5px; top: 7px; } + + &__favorite { + align-items: center; + display: flex; + margin-left: 10px; + opacity: 0.6; + } } diff --git a/src/frontend/packages/core/src/shared/components/sidepanel-preview/sidepanel-preview.component.ts b/src/frontend/packages/core/src/shared/components/sidepanel-preview/sidepanel-preview.component.ts index 0dbf97b137..4e36e158d2 100644 --- a/src/frontend/packages/core/src/shared/components/sidepanel-preview/sidepanel-preview.component.ts +++ b/src/frontend/packages/core/src/shared/components/sidepanel-preview/sidepanel-preview.component.ts @@ -1,5 +1,7 @@ +import { Portal } from '@angular/cdk/portal'; import { Component, Input } from '@angular/core'; +import { IFavoriteMetadata, UserFavorite } from '../../../../../store/src/types/user-favorites.types'; import { SidePanelService } from '../../services/side-panel.service'; @Component({ @@ -12,5 +14,10 @@ export class SidepanelPreviewComponent { @Input() title: string; + @Input() + favorite: UserFavorite; + + @Input() header: Portal; + constructor(public sidePanelService: SidePanelService) { } } 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 c58dac3de2..67113fadf2 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 @@ -92,7 +92,8 @@ export class SidePanelService { this.showMode(SidePanelMode.Modal, component, props, componentFactoryResolver); } - private open() { + // Re-open the panel with its current contents + public open() { this.openedSubject.next(true); this.document.addEventListener('keydown', this.onKeyDown); } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/analysis-report-viewer/analysis-report-selector/analysis-report-selector.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/analysis-report-viewer/analysis-report-selector/analysis-report-selector.component.ts index 37b180cf3d..7c0fe52450 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/analysis-report-viewer/analysis-report-selector/analysis-report-selector.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/analysis-report-viewer/analysis-report-selector/analysis-report-selector.component.ts @@ -37,7 +37,7 @@ export class AnalysisReportSelectorComponent implements OnInit, OnDestroy { } ngOnInit() { - this.analyzers$ = this.analysisService.getByPath(this.endpoint, this.path).pipe( + this.analyzers$ = this.analysisService.getByPath(this.endpoint, this.path, true).pipe( map(reports => { const res = []; if (this.allowNone) { 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 35262b20c9..2d70d949da 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 @@ -50,7 +50,7 @@ export class KubernetesHomeCardComponent implements OnInit { }, { title: 'View Namespaces', - link: ['/kubernetes', guid, 'namespaces'], + link: ['/kubernetes', guid, 'resource', 'namespace'], icon: 'namespace', iconFont: 'stratos-icons' } 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 e9ede6b81a..1d3d6e8eb4 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-entity-generator.ts @@ -17,6 +17,7 @@ import { } from '../../../store/src/entity-catalog/entity-catalog.types'; import { EndpointAuthTypeConfig, EndpointType } from '../../../store/src/extension-types'; import { metricEntityType } from '../../../store/src/helpers/stratos-entity-factory'; +import { entityFetchedWithoutError } from '../../../store/src/operators'; import { IFavoriteMetadata } from '../../../store/src/types/user-favorites.types'; import { KubernetesAWSAuthFormComponent } from './auth-forms/kubernetes-aws-auth-form/kubernetes-aws-auth-form.component'; import { @@ -178,21 +179,36 @@ class KubeResourceEntityHelper { defn.apiNamespaced = true; } + const schema = kubernetesEntityFactory(defn.type); const d: IStratosEntityDefinition = { ...defn, endpoint: endpointDefinition, - schema: kubernetesEntityFactory(defn.type), + schema, iconFont: defn.iconFont || 'stratos-icons', labelPlural: defn.labelPlural || `${defn.label}s` }; - if (defn.getKubeCatalogEntity) { - return defn.getKubeCatalogEntity(d); - } else { - return new StratosCatalogEntity(d, { - actionBuilders: createKubeResourceActionBuilder(d.type) as unknown as C - }); + const entity = defn.getKubeCatalogEntity ? defn.getKubeCatalogEntity(d) : new StratosCatalogEntity(d, { + actionBuilders: createKubeResourceActionBuilder(d.type) as unknown as C + }); + + if (defn.canFavorite && defn.getIsValid) { + entity.builders.entityBuilder = { + getIsValid: defn.getIsValid, + getMetadata: (resource: any) => { + return { + endpointId: resource.kubeGuid, + guid: resource.metadata.uid, + kubeGuid: resource.kubeGuid, + name: resource.metadata.name, + }; + }, + getLink: metadata => `/kubernetes/${metadata.endpointId}/resource/${defn.type}`, + getGuid: resource => schema.getId(resource), + }; } + + return entity; } } @@ -239,7 +255,7 @@ export class KubeEntityCatalog { BaseEndpointAuth.UsernamePassword, kubeAuthTypeMap[KubeEndpointAuthTypes.TOKEN], ], - getEndpointIdFromEntity: (entity) => entity.kubeGuid, + getEndpointIdFromEntity: (entity) => entity.kubeGuid || entity.metadata?.kubeId, renderPriority: 4, subTypes: [ { @@ -301,7 +317,6 @@ export class KubeEntityCatalog { endpointDef, favorite => `/kubernetes/${favorite.endpointId}` ); - this.statefulSet = this.generateStatefulSetsEntity(endpointDef); this.pod = KubeResourceEntityHelper.generate(endpointDef, { type: kubernetesPodsEntityType, @@ -324,9 +339,12 @@ export class KubeEntityCatalog { apiVersion: '/api/v1', apiName: 'namespaces', apiNamespaced: false, + canFavorite: true, getKubeCatalogEntity: (definition) => new StratosCatalogEntity( definition, { actionBuilders: kubeNamespaceActionBuilders } ), + getIsValid: (favorite) => + kubeEntityCatalog.namespace.api.get(favorite.metadata.name, favorite.endpointId).pipe(entityFetchedWithoutError()), listColumns: [ { header: 'Status', @@ -365,8 +383,6 @@ export class KubeEntityCatalog { ] }); this.metrics = this.generateMetricEntity(endpointDef); - - this.secrets = KubeResourceEntityHelper.generate(endpointDef, { type: 'secrets', icon: 'config_maps', @@ -481,7 +497,6 @@ export class KubeEntityCatalog { apiVersion: '/apis/batch/v1', apiName: 'jobs', }); - } public allKubeEntities(): StratosBaseCatalogEntity[] { @@ -552,8 +567,6 @@ export class KubeEntityCatalog { endpoint: endpointDefinition, }); } - - } /** diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-analysis-report/kubernetes-namespace-analysis-report.component.html b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-analysis-report/kubernetes-namespace-analysis-report.component.html index 650e5a18aa..8c2e5ae5d6 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-analysis-report/kubernetes-namespace-analysis-report.component.html +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-analysis-report/kubernetes-namespace-analysis-report.component.html @@ -1,4 +1,4 @@ - + diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-analysis-report/kubernetes-namespace-analysis-report.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-analysis-report/kubernetes-namespace-analysis-report.component.ts index cdaa70db35..75538409a0 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-analysis-report/kubernetes-namespace-analysis-report.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-analysis-report/kubernetes-namespace-analysis-report.component.ts @@ -4,6 +4,7 @@ import { Subject } from 'rxjs'; 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 { AnalysisReport } from '../../store/kube.types'; @Component({ @@ -11,7 +12,10 @@ import { AnalysisReport } from '../../store/kube.types'; templateUrl: './kubernetes-namespace-analysis-report.component.html', styleUrls: ['./kubernetes-namespace-analysis-report.component.scss'], providers: [ - KubernetesAnalysisService + KubernetesService, + KubernetesEndpointService, + KubernetesNamespaceService, + KubernetesAnalysisService, ] }) export class KubernetesNamespaceAnalysisReportComponent { @@ -26,6 +30,8 @@ export class KubernetesNamespaceAnalysisReportComponent { noReportsAvailable = false; + breadcrumbs = []; + constructor( public analyzerService: KubernetesAnalysisService, public endpointService: KubernetesEndpointService, @@ -34,6 +40,12 @@ export class KubernetesNamespaceAnalysisReportComponent { this.endpointID = this.endpointService.kubeGuid; this.path = `${this.kubeNamespaceService.namespaceName}`; this.report$.next(null); + + this.breadcrumbs = [ + { value: 'Analysis' }, + { value: this.path }, + ]; + } public analysisChanged(report) { diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-pods/kubernetes-namespace-pods.component.html b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-pods/kubernetes-namespace-pods.component.html deleted file mode 100644 index dd2b6cb351..0000000000 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-pods/kubernetes-namespace-pods.component.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-pods/kubernetes-namespace-pods.component.scss b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-pods/kubernetes-namespace-pods.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-pods/kubernetes-namespace-pods.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-pods/kubernetes-namespace-pods.component.ts deleted file mode 100644 index a9127d468f..0000000000 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-pods/kubernetes-namespace-pods.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Component } from '@angular/core'; - -import { ListConfig } from '../../../../../core/src/shared/components/list/list.component.types'; -import { - KubernetesNamespacePodsListConfigService, -} from '../../list-types/kubernetes-namespace-pods/kubernetes-namespace-pods-list-config.service'; - -@Component({ - selector: 'app-kubernetes-namespace-pods', - templateUrl: './kubernetes-namespace-pods.component.html', - styleUrls: ['./kubernetes-namespace-pods.component.scss'], - providers: [{ - provide: ListConfig, - useClass: KubernetesNamespacePodsListConfigService, - }] -}) -export class KubernetesNamespacePodsComponent { } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component.html b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component.html new file mode 100644 index 0000000000..3e0622bcb9 --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component.html @@ -0,0 +1,4 @@ +
+ assignment + Namespace Analysis +
diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component.scss b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component.scss new file mode 100644 index 0000000000..92c28a4eaf --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component.scss @@ -0,0 +1,14 @@ +.namespace-preview { + align-items: center; + display: flex; + margin: 10px 0; + + mat-icon { + font-size: 18px; + height: 18px; + line-height: 18px; + margin-right: 5px; + opacity: 0.7; + width: 18px;; + } +} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component.spec.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component.spec.ts new file mode 100644 index 0000000000..6bb47d4562 --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component.spec.ts @@ -0,0 +1,28 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { KubernetesBaseTestModules } from '../../kubernetes.testing.module'; +import { KubernetesNamespacePreviewComponent } from './kubernetes-namespace-preview.component'; + +describe('KubernetesNamespacePreviewComponent', () => { + let component: KubernetesNamespacePreviewComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ KubernetesNamespacePreviewComponent ], + imports: [...KubernetesBaseTestModules], + + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(KubernetesNamespacePreviewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component.ts new file mode 100644 index 0000000000..4064e192cb --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component.ts @@ -0,0 +1,50 @@ +import { Component } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Store } from '@ngrx/store'; +import { AppState } from '@stratosui/store'; +import { Observable } from 'rxjs'; + +import { PreviewableComponent } from '../../../../../core/src/shared/previewable-component'; +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'; + +@Component({ + selector: 'app-kubernetes-namespace-preview', + templateUrl: './kubernetes-namespace-preview.component.html', + styleUrls: ['./kubernetes-namespace-preview.component.scss'], + providers: [ + { + provide: BaseKubeGuid, + useFactory: (activatedRoute: ActivatedRoute) => { + return { + guid: activatedRoute.snapshot.params.endpointId + }; + }, + deps: [ + ActivatedRoute + ] + }, + KubernetesService, + KubernetesEndpointService, + KubernetesNamespaceService, + KubernetesAnalysisService, + ] +}) +export class KubernetesNamespacePreviewComponent implements PreviewableComponent { + + showAnalysis$: Observable; + + link: string; + + constructor(store: Store) { + this.showAnalysis$ = KubernetesAnalysisService.isAnalysisEnabled(store); + } + + setProps(props: { [key: string]: any; }): void { + const { resource, endpointId } = props; + this.link = `/kubernetes/${endpointId}/resource/namespace/${resource.metadata.name}/analysis`; + } +} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component.html b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component.html deleted file mode 100644 index dd2b6cb351..0000000000 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component.html +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component.scss b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component.spec.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component.spec.ts deleted file mode 100644 index 68f5dd3509..0000000000 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { TabNavService } from '../../../../../core/src/tab-nav.service'; -import { BaseKubeGuid } from '../../kubernetes-page.types'; -import { KubernetesBaseTestModules } from '../../kubernetes.testing.module'; -import { KubernetesEndpointService } from '../../services/kubernetes-endpoint.service'; -import { KubernetesNamespaceService } from '../../services/kubernetes-namespace.service'; -import { KubernetesNamespaceServicesComponent } from './kubernetes-namespace-services.component'; - -describe('KubernetesNamespaceServicesComponent', () => { - let component: KubernetesNamespaceServicesComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [KubernetesNamespaceServicesComponent], - imports: [...KubernetesBaseTestModules], - providers: [ - TabNavService, - BaseKubeGuid, - KubernetesNamespaceService, - KubernetesEndpointService - ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(KubernetesNamespaceServicesComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component.ts deleted file mode 100644 index 02a76e577e..0000000000 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Component } from '@angular/core'; - -import { ListConfig } from '../../../../../core/src/shared/components/list/list.component.types'; -import { - KubernetesNamespaceServicesListConfig, -} from '../../list-types/kubernetes-namespace-services/kubernetes-namespace-services-list-config.service'; - -@Component({ - selector: 'app-kubernetes-namespace-services', - templateUrl: './kubernetes-namespace-services.component.html', - styleUrls: ['./kubernetes-namespace-services.component.scss'], - providers: [{ - provide: ListConfig, - useClass: KubernetesNamespaceServicesListConfig, - }] -}) -export class KubernetesNamespaceServicesComponent { - - -} 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 deleted file mode 100644 index 749c7af66f..0000000000 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.html +++ /dev/null @@ -1,6 +0,0 @@ - -

{{ kubeNamespaceService.namespaceName }}

-
-
-
- diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.scss b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.scss deleted file mode 100644 index e69de29bb2..0000000000 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 deleted file mode 100644 index 044a84b05b..0000000000 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-namespace/kubernetes-namespace.component.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { Component } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { Observable } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; - -import { IHeaderBreadcrumb } from '../../../../core/src/shared/components/page-header/page-header.types'; -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'; -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({ - selector: 'app-kubernetes-namespace', - templateUrl: './kubernetes-namespace.component.html', - styleUrls: ['./kubernetes-namespace.component.scss'], - providers: [ - { - provide: BaseKubeGuid, - useFactory: (activatedRoute: ActivatedRoute) => { - return { - guid: activatedRoute.snapshot.params.endpointId - }; - }, - deps: [ - ActivatedRoute - ] - }, - KubernetesService, - KubernetesEndpointService, - KubernetesNamespaceService, - KubernetesAnalysisService, - ] -}) -export class KubernetesNamespaceComponent { - - tabLinks = []; - - public breadcrumbs$: Observable; - - constructor( - public kubeEndpointService: KubernetesEndpointService, - public kubeNamespaceService: KubernetesNamespaceService, - public analysisService: KubernetesAnalysisService, - private userFavoriteManager: UserFavoriteManager, - ) { - this.breadcrumbs$ = kubeEndpointService.endpoint$.pipe( - map(endpoint => ([{ - breadcrumbs: [ - { value: endpoint.entity.name, routerLink: `/kubernetes/${endpoint.entity.guid}/namespaces` }, - ] - }]) - ) - ); - - this.tabLinks = [ - { link: 'analysis', label: 'Analysis', icon: 'assignment', hidden$: this.analysisService.hideAnalysis$ }, - { link: 'services', label: 'Services', icon: 'service', iconFont: 'stratos-icons' }, - ]; - - this.tabLinks.push(...this.getTabsFromEntityConfig(true)); - } - - public favorite$ = this.kubeNamespaceService.namespace$.pipe( - filter(app => !!app), - map(namespace => this.userFavoriteManager.getFavorite( - { - kubeGuid: this.kubeEndpointService.baseKube.guid, - ...namespace, - prettyText: 'Kubernetes Namespace', - }, - kubernetesNamespacesEntityType, - KUBERNETES_ENDPOINT_TYPE - )) - ); - - private getTabsFromEntityConfig(namespaced: boolean = true) { - return []; - // const entityNames = Object.getOwnPropertyNames(kubeEntityCatalog); - // const tabsFromRouterConfig = []; - - // // Get the tabs from the router configuration - // kubeEntityCatalog.allKubeEntities().forEach(catalogEntity => { - // if (catalogEntity) { - // const defn = catalogEntity.definition as KubeResourceEntityDefinition; - // if (defn.apiNamespaced === namespaced) { - // tabsFromRouterConfig.push({ - // link: defn.route || `resource/${key}`, - // label: defn.labelTab || defn.labelPlural, - // icon: defn.icon, - // iconFont: defn.iconFont, - // }); - // } - // } - // }); - - // tabsFromRouterConfig.sort((a, b) => a.label.localeCompare(b.label)); - // return tabsFromRouterConfig; - } - -} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource-viewer/kubernetes-resource-viewer.component.html b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource-viewer/kubernetes-resource-viewer.component.html index c3ebbdd9e1..b94c494c62 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource-viewer/kubernetes-resource-viewer.component.html +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource-viewer/kubernetes-resource-viewer.component.html @@ -1,4 +1,4 @@ - +
@@ -31,6 +31,17 @@ - + + + + +
+ + + +
+
- \ No newline at end of file +
\ No newline at end of file diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource-viewer/kubernetes-resource-viewer.component.scss b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource-viewer/kubernetes-resource-viewer.component.scss index a427bd0214..2f691750e2 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource-viewer/kubernetes-resource-viewer.component.scss +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource-viewer/kubernetes-resource-viewer.component.scss @@ -1,5 +1,9 @@ +$icon_size: 20px; + .resource-preview { + font-size: 14px; + &__labels { display: table; } @@ -24,7 +28,19 @@ app-metadata-item { margin-right: 40px; } + } + &__header { + display: flex; + justify-content: flex-end; + + mat-icon { + font-size: $icon_size; + height: $icon_size; + line-height: $icon_size; + opacity: 0.7; + width: $icon_size; + } } &__metrics { diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource-viewer/kubernetes-resource-viewer.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource-viewer/kubernetes-resource-viewer.component.ts index 3f868ec9ef..6dd1e2b08c 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource-viewer/kubernetes-resource-viewer.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource-viewer/kubernetes-resource-viewer.component.ts @@ -1,18 +1,46 @@ -import { Component } from '@angular/core'; +import { Portal, TemplatePortal } from '@angular/cdk/portal'; +import { + AfterViewInit, + Component, + ComponentFactory, + ComponentFactoryResolver, + ComponentRef, + OnDestroy, + TemplateRef, + ViewChild, + ViewContainerRef, +} from '@angular/core'; import moment from 'moment'; import { Observable, of } from 'rxjs'; import { filter, first, map, publishReplay, refCount, switchMap } from 'rxjs/operators'; import { EndpointsService } from '../../../../core/src/core/endpoints.service'; +import { ConfirmationDialogConfig } from '../../../../core/src/shared/components/confirmation-dialog.config'; import { PreviewableComponent } from '../../../../core/src/shared/previewable-component'; +import { SnackBarService } from '../../../../core/src/shared/services/snackbar.service'; +import { StratosCatalogEntity } from '../../../../store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity'; +import { entityDeleted } from '../../../../store/src/operators'; +import { IFavoriteMetadata, UserFavorite } from '../../../../store/src/types/user-favorites.types'; +import { KUBERNETES_ENDPOINT_TYPE } from '../kubernetes-entity-factory'; import { KubernetesEndpointService } from '../services/kubernetes-endpoint.service'; -import { BasicKubeAPIResource, KubeAPIResource, KubeStatus } from '../store/kube.types'; +import { KubeResourceActionBuilders } from '../store/action-builders/kube-resource.action-builder'; +import { BasicKubeAPIResource, KubeAPIResource, KubeResourceEntityDefinition, KubeStatus } from '../store/kube.types'; +import { ConfirmationDialogService } from './../../../../core/src/shared/components/confirmation-dialog.service'; +import { SidePanelService } from './../../../../core/src/shared/services/side-panel.service'; +import { entityCatalog } from './../../../../store/src/entity-catalog/entity-catalog'; +import { UserFavoriteManager } from './../../../../store/src/user-favorite-manager'; + +export interface KubernetesResourceViewerComponentConfig { + resource: BasicKubeAPIResource; +} export interface KubernetesResourceViewerConfig { title: string; analysis?: any; resource$: Observable; resourceKind: string; + component?: any; + definition?: any; } interface KubernetesResourceViewerResource { @@ -31,13 +59,18 @@ interface KubernetesResourceViewerResource { templateUrl: './kubernetes-resource-viewer.component.html', styleUrls: ['./kubernetes-resource-viewer.component.scss'] }) -export class KubernetesResourceViewerComponent implements PreviewableComponent { +export class KubernetesResourceViewerComponent implements PreviewableComponent, OnDestroy, AfterViewInit { constructor( private endpointsService: EndpointsService, - private kubeEndpointService: KubernetesEndpointService - ) { - } + private kubeEndpointService: KubernetesEndpointService, + private resolver: ComponentFactoryResolver, + private userFavoriteManager: UserFavoriteManager, + private viewContainerRef: ViewContainerRef, + private confirmDialog: ConfirmationDialogService, + private sidePanelService: SidePanelService, + private snackBarService: SnackBarService, + ) { } public title: string; public resource$: Observable; @@ -48,9 +81,51 @@ export class KubernetesResourceViewerComponent implements PreviewableComponent { private analysis; public alerts; + public favorite: UserFavorite; + + // Custom component + @ViewChild('customComponent', { read: ViewContainerRef, static: false }) customComponentContainer; + componentRef: ComponentRef; + + component: any; + + data: any; // TODO: Typing + + @ViewChild('header', { static: false }) templatePortalContent: TemplateRef; + headerContent: Portal; + + ngOnDestroy() { + this.removeCustomComponent(); + } + + removeCustomComponent() { + if (this.customComponentContainer) { + this.customComponentContainer.clear(); + } + if (this.componentRef) { + this.componentRef.destroy(); + } + } + + createCustomComponent() { + this.removeCustomComponent(); + if (this.component && this.customComponentContainer) { + const factory: ComponentFactory = this.resolver.resolveComponentFactory(this.component); + this.componentRef = this.customComponentContainer.createComponent(factory); + this.componentRef.instance.setProps(this.data); + } + } + + ngAfterViewInit() { + this.createCustomComponent(); + setTimeout(() => this.headerContent = new TemplatePortal(this.templatePortalContent, this.viewContainerRef), 0); + } + setProps(props: KubernetesResourceViewerConfig) { this.title = props.title; this.analysis = props.analysis; + this.component = props.component; + this.resource$ = props.resource$.pipe( filter(item => !!item), map((item: (KubeAPIResource | KubeStatus)) => { @@ -98,6 +173,16 @@ export class KubernetesResourceViewerComponent implements PreviewableComponent { /* tslint:disable-next-line:no-string-literal */ resource.apiVersion = item['apiVersion'] || fallback.apiVersion || this.getVersionFromSelfLink(item.metadata['selfLink']); + this.component = props.component; + this.data = { + endpointId: this.getEndpointId(item), + resource: item, + definition: props.definition + }; + this.createCustomComponent(); + + setTimeout(() => this.setFavorite(props.definition, item), 0); + // Apply analysis if there is one - if this is a k8s resource (i.e. not a container) if (item.metadata) { this.applyAnalysis(resource); @@ -128,6 +213,7 @@ export class KubernetesResourceViewerComponent implements PreviewableComponent { ]; }) ); + this.createCustomComponent(); } private getVersionFromSelfLink(url: string): string { @@ -139,7 +225,7 @@ export class KubernetesResourceViewerComponent implements PreviewableComponent { } private getEndpointId(res): string { - return this.kubeEndpointService.kubeGuid || res.endpointId || res.metadata.kubeId; + return this.kubeEndpointService?.kubeGuid || res.endpointId || res.metadata?.kubeId; } private applyAnalysis(resource) { @@ -152,4 +238,48 @@ export class KubernetesResourceViewerComponent implements PreviewableComponent { } } + private setFavorite(defn: KubeResourceEntityDefinition, item: any) { + if (defn) { + const entityDefn = entityCatalog.getEntity(KUBERNETES_ENDPOINT_TYPE, defn.type); + const canFav = this.userFavoriteManager.canFavoriteEntityType(entityDefn); + if (canFav) { + this.favorite = this.userFavoriteManager.getFavorite(item, defn.type, KUBERNETES_ENDPOINT_TYPE); + } + } + } + + // Warn about deletion and then delete the resource if confirmed + public deleteWarn() { + // Namespace vs Pod definition in different places + const defn = (this.data.definition?.definition || this.data.definition) as KubeResourceEntityDefinition; + this.sidePanelService.hide(); + const confirmation = new ConfirmationDialogConfig( + `Delete ${defn.label}`, + `Are you sure you want to delete "${this.data.resource.metadata.name}" ?`, + 'Delete', + true, + ); + this.confirmDialog.openWithCancel(confirmation, + () => { + const catalogEntity = entityCatalog.getEntityFromKey(entityCatalog.getEntityKey(KUBERNETES_ENDPOINT_TYPE, defn.type)) as + StratosCatalogEntity; + catalogEntity.api.deleteResource( + this.data.resource, + this.data.endpointId, + this.data.resource.metadata.name, + this.data.resource.metadata.namespace + ).pipe( + entityDeleted(), + first() + ).subscribe((result) => { + const msg = result.error ? `Could not delete reosource: ${result.error}` : `Deleted resource '${this.data.resource.metadata.name}'`; + this.snackBarService.show(msg); + } + ); + }, + () => { + this.sidePanelService.open(); + } + ); + } } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource/kubernetes-resource-list/kubernetes-resource-list.component.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource/kubernetes-resource-list/kubernetes-resource-list.component.ts index 361fbf74ac..c9548e4b0d 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource/kubernetes-resource-list/kubernetes-resource-list.component.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-resource/kubernetes-resource-list/kubernetes-resource-list.component.ts @@ -16,12 +16,12 @@ import { ListViewTypes } from '../../../../../core/src/shared/components/list/li import { entityCatalog } from '../../../../../store/src/public-api'; import { KUBERNETES_ENDPOINT_TYPE } from '../../kubernetes-entity-factory'; import { kubeEntityCatalog } from '../../kubernetes-entity-generator'; -import { KubernetesListConfigService } from '../../kubernetes-list-service'; import { BaseKubeGuid } from '../../kubernetes-page.types'; import { KubernetesResourceViewerComponent, KubernetesResourceViewerConfig, } from '../../kubernetes-resource-viewer/kubernetes-resource-viewer.component'; +import { KubernetesUIConfigService } from '../../kubernetes-ui-service'; import { defaultHelmKubeListPageSize } from '../../list-types/kube-helm-list-types'; import { createKubeAgeColumn } from '../../list-types/kube-list.helper'; import { @@ -61,7 +61,7 @@ export class KubernetesResourceListComponent implements OnDestroy { route: ActivatedRoute, router: Router, private kubeId: BaseKubeGuid, - private listConfigService: KubernetesListConfigService + private uiConfigService: KubernetesUIConfigService ) { // Entity Catalog Key can be specified in the route config this.entityCatalogKey = route.snapshot.data.entityCatalogKey; @@ -111,7 +111,7 @@ export class KubernetesResourceListComponent implements OnDestroy { const provider = new ActionListConfigProvider(this.store, action); const listConfigName = catalogEntity.definition ? catalogEntity.definition.listConfig : null; - let listConfig: any = this.listConfigService.get(listConfigName); + let listConfig: any = this.uiConfigService.listConfig.get(listConfigName); if (!listConfig) { listConfig = { pageSizeOptions: defaultHelmKubeListPageSize, @@ -135,6 +135,7 @@ export class KubernetesResourceListComponent implements OnDestroy { } private getColumns(definition: KubeResourceEntityDefinition): ITableColumn[] { + const component = this.uiConfigService.previewComponent.get(definition.type); let columns: Array> = [ // Name { @@ -153,7 +154,9 @@ export class KubernetesResourceListComponent implements OnDestroy { sidePanelConfig: { title: resource.metadata.name, resourceKind: definition.label, - resource$: of(resource) + resource$: of(resource), + component, + definition, } }); } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-ui-service.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-ui-service.ts new file mode 100644 index 0000000000..606757ca42 --- /dev/null +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes-ui-service.ts @@ -0,0 +1,34 @@ +import { Injectable, Type } from '@angular/core'; +import { ISimpleListConfig } from 'frontend/packages/core/src/shared/components/list/list.component.types'; + +import { PreviewableComponent } from '../../../core/src/shared/previewable-component'; + +class ConfigHolder { + + private configs: T = {} as T; + + set(name: string, config: T) { + this.configs[name] = config; + } + + get(name: string): Y { + return name ? this.configs[name] : undefined; + } +} + +// Holder for UI configurations - e.g. list configurations +// This allows us to reference them by name and lazy-load the configs yet reference them +// in an entity defintion that may not have been lazy-loaded + +@Injectable({ + providedIn: 'root', +}) +export class KubernetesUIConfigService { + + // List configurations + public listConfig = new ConfigHolder>(); + + // Side Panel Preview Resource components + public previewComponent = new ConfigHolder>(); + +} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes.module.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes.module.ts index c453d11135..f56326ae4c 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes.module.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes.module.ts @@ -26,17 +26,13 @@ import { KubedashConfigurationComponent, } from './kubernetes-dashboard/kubedash-configuration/kubedash-configuration.component'; import { KubernetesDashboardTabComponent } from './kubernetes-dashboard/kubernetes-dashboard.component'; -import { KubernetesListConfigService } from './kubernetes-list-service'; +import { kubernetesNamespacesEntityType } from './kubernetes-entity-factory'; import { KubernetesNamespaceAnalysisReportComponent, } from './kubernetes-namespace/kubernetes-namespace-analysis-report/kubernetes-namespace-analysis-report.component'; import { - KubernetesNamespacePodsComponent, -} from './kubernetes-namespace/kubernetes-namespace-pods/kubernetes-namespace-pods.component'; -import { - KubernetesNamespaceServicesComponent, -} from './kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component'; -import { KubernetesNamespaceComponent } from './kubernetes-namespace/kubernetes-namespace.component'; + KubernetesNamespacePreviewComponent, +} from './kubernetes-namespace/kubernetes-namespace-preview/kubernetes-namespace-preview.component'; import { KubernetesNodeMetricStatsCardComponent, } from './kubernetes-node/kubernetes-node-metrics/kubernetes-node-metric-stats-card/kubernetes-node-metric-stats-card.component'; @@ -52,6 +48,7 @@ import { KubernetesNodeComponent } from './kubernetes-node/kubernetes-node.compo import { BaseKubeGuid } from './kubernetes-page.types'; import { KubernetesResourceViewerComponent } from './kubernetes-resource-viewer/kubernetes-resource-viewer.component'; import { KubernetesTabBaseComponent } from './kubernetes-tab-base/kubernetes-tab-base.component'; +import { KubernetesUIConfigService } from './kubernetes-ui-service'; import { KubernetesRoutingModule } from './kubernetes.routing'; import { KubernetesComponent } from './kubernetes/kubernetes.component'; import { AnalysisStatusCellComponent } from './list-types/analysis-status-cell/analysis-status-cell.component'; @@ -168,9 +165,6 @@ import { KubernetesSummaryTabComponent } from './tabs/kubernetes-summary-tab/kub KubernetesNodeSimpleMetricComponent, ConditionCellComponent, KubernetesNamespaceLinkComponent, - KubernetesNamespaceComponent, - KubernetesNamespacePodsComponent, - KubernetesNamespaceServicesComponent, KubeNamespacePodCountComponent, NodePodCountComponent, KubernetesServicePortsComponent, @@ -193,6 +187,7 @@ import { KubernetesSummaryTabComponent } from './tabs/kubernetes-summary-tab/kub KubeScoreReportViewerComponent, AnalysisStatusCellComponent, KubernetesNamespaceAnalysisReportComponent, + KubernetesNamespacePreviewComponent, ], providers: [ KubernetesService, @@ -233,13 +228,16 @@ import { KubernetesSummaryTabComponent } from './tabs/kubernetes-summary-tab/kub ResourceAlertPreviewComponent, ResourceAlertViewComponent, AnalysisStatusCellComponent, + KubernetesNamespacePreviewComponent, ] }) export class KubernetesModule { - constructor(listConfigService: KubernetesListConfigService) { - listConfigService.set('k8s-pods', new KubernetesPodsListConfig()); - listConfigService.set('k8s-services', new KubernetesServicesListConfig()); + constructor(uiConfigService: KubernetesUIConfigService) { + uiConfigService.listConfig.set('k8s-pods', new KubernetesPodsListConfig()); + uiConfigService.listConfig.set('k8s-services', new KubernetesServicesListConfig()); + + uiConfigService.previewComponent.set(kubernetesNamespacesEntityType, KubernetesNamespacePreviewComponent); } } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes.routing.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes.routing.ts index db092316d9..7c7868ee30 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes.routing.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes.routing.ts @@ -9,13 +9,6 @@ import { KubernetesDashboardTabComponent } from './kubernetes-dashboard/kubernet import { KubernetesNamespaceAnalysisReportComponent, } from './kubernetes-namespace/kubernetes-namespace-analysis-report/kubernetes-namespace-analysis-report.component'; -import { - KubernetesNamespacePodsComponent, -} from './kubernetes-namespace/kubernetes-namespace-pods/kubernetes-namespace-pods.component'; -import { - KubernetesNamespaceServicesComponent, -} from './kubernetes-namespace/kubernetes-namespace-services/kubernetes-namespace-services.component'; -import { KubernetesNamespaceComponent } from './kubernetes-namespace/kubernetes-namespace.component'; import { KubernetesNodeMetricsComponent } from './kubernetes-node/kubernetes-node-metrics/kubernetes-node-metrics.component'; import { KubernetesNodePodsComponent } from './kubernetes-node/kubernetes-node-pods/kubernetes-node-pods.component'; import { KubernetesNodeComponent } from './kubernetes-node/kubernetes-node.component'; @@ -75,34 +68,6 @@ const kubernetes: Routes = [{ } ] }, -// TODO: RC these can be removed? -{ - path: ':endpointId/namespaces/:namespaceName', - component: KubernetesNamespaceComponent, // TODO: RC This component, and others in here, should be removed if route goes - children: [ - { - path: '', - redirectTo: 'pods', - pathMatch: 'full' - }, - { - path: 'pods', - component: KubernetesNamespacePodsComponent - }, - { - path: 'services', - component: KubernetesNamespaceServicesComponent - }, - { - path: 'analysis', - component: KubernetesNamespaceAnalysisReportComponent - }, - { - path: 'resource/:resource', - loadChildren: () => import('./kubernetes-resource/generic-resource.module').then(m => m.KubernetesGenericResourceModule), - }, - ] -}, { path: ':endpointId', component: KubernetesTabBaseComponent, @@ -136,6 +101,11 @@ const kubernetes: Routes = [{ path: 'analysis/info', component: KubernetesAnalysisInfoComponent }, + { + path: 'resource/namespace/:namespaceName/analysis', + pathMatch: 'full', + component: KubernetesNamespaceAnalysisReportComponent + }, { path: 'resource/:resource', loadChildren: () => import('./kubernetes-resource/generic-resource.module').then(m => m.KubernetesGenericResourceModule), diff --git a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes.setup.module.ts b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes.setup.module.ts index 55f487759c..d1b145edd2 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/kubernetes.setup.module.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/kubernetes.setup.module.ts @@ -44,6 +44,7 @@ import { KUBERNETES_ENDPOINT_TYPE } from './kubernetes-entity-factory'; import { kubeEntityCatalog } from './kubernetes-entity-generator'; import { KubernetesListConfigService } from './kubernetes-list-service'; import { BaseKubeGuid } from './kubernetes-page.types'; +import { KubernetesUIConfigService } from './kubernetes-ui-service'; import { KubernetesStoreModule } from './kubernetes.store.module'; import { KubernetesEndpointService } from './services/kubernetes-endpoint.service'; @@ -74,7 +75,7 @@ import { KubernetesEndpointService } from './services/kubernetes-endpoint.servic providers: [ BaseKubeGuid, KubernetesEndpointService, - KubernetesListConfigService, + KubernetesUIConfigService, ], entryComponents: [ KubernetesCertsAuthFormComponent, diff --git a/src/frontend/packages/kubernetes/src/kubernetes/list-types/kubernetes-pods/kubernetes-pods-list-config.service.ts b/src/frontend/packages/kubernetes/src/kubernetes/list-types/kubernetes-pods/kubernetes-pods-list-config.service.ts index d1595ace69..809dac6c9a 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/list-types/kubernetes-pods/kubernetes-pods-list-config.service.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/list-types/kubernetes-pods/kubernetes-pods-list-config.service.ts @@ -13,6 +13,8 @@ import { import { KubernetesPod } from '../../store/kube.types'; import { defaultHelmKubeListPageSize } from '../kube-helm-list-types'; import { createKubeAgeColumn } from '../kube-list.helper'; +import { entityCatalog } from './../../../../../store/src/entity-catalog/entity-catalog'; +import { KUBERNETES_ENDPOINT_TYPE, kubernetesPodsEntityType } from './../../kubernetes-entity-factory'; import { KubernetesPodContainersComponent } from './kubernetes-pod-containers/kubernetes-pod-containers.component'; import { KubernetesPodStatusComponent } from './kubernetes-pod-status/kubernetes-pod-status.component'; @@ -50,7 +52,8 @@ export abstract class BaseKubernetesPodsListConfigService implements ISimpleList sidePanelConfig: { title: pod.metadata.name, resourceKind: 'pod', - resource$: of(pod) + resource$: of(pod), + definition: entityCatalog.getEntity(KUBERNETES_ENDPOINT_TYPE, kubernetesPodsEntityType) } }) }, diff --git a/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes.analysis.service.ts b/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes.analysis.service.ts index cadbc43b70..8d2d48e50e 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes.analysis.service.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/services/kubernetes.analysis.service.ts @@ -34,6 +34,14 @@ export class KubernetesAnalysisService { private action: GetAnalysisReports; + public static isAnalysisEnabled(store: Store): Observable { + // Is the backend plugin available? + const enabled$ = store.select('auth').pipe( + map(auth => auth.sessionData.plugins && auth.sessionData.plugins.analysis) + ); + return enabled$.pipe(startWith(false)); + } + constructor( public kubeEndpointService: KubernetesEndpointService, public activatedRoute: ActivatedRoute, @@ -43,14 +51,8 @@ export class KubernetesAnalysisService { this.kubeGuid = kubeEndpointService.kubeGuid || getHelmReleaseDetailsFromGuid(activatedRoute.snapshot.params.guid).endpointId; // Is the backend plugin available? - this.enabled$ = this.store.select('auth').pipe( - map(auth => auth.sessionData.plugins && auth.sessionData.plugins.analysis) - ); - - this.hideAnalysis$ = this.enabled$.pipe( - map(enabled => !enabled), - startWith(true), - ); + this.enabled$ = KubernetesAnalysisService.isAnalysisEnabled(this.store); + this.hideAnalysis$ = this.enabled$.pipe(map(enabled => !enabled)); const allEngines = { popeye: diff --git a/src/frontend/packages/kubernetes/src/kubernetes/store/action-builders/kube-resource.action-builder.ts b/src/frontend/packages/kubernetes/src/kubernetes/store/action-builders/kube-resource.action-builder.ts index 0b136365ce..4cb976559e 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/store/action-builders/kube-resource.action-builder.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/store/action-builders/kube-resource.action-builder.ts @@ -1,5 +1,11 @@ import { OrchestratedActionBuilders } from '../../../../../store/src/entity-catalog/action-orchestrator/action-orchestrator'; -import { GetKubernetesResource, GetKubernetesResources, GetKubernetesResourcesInNamespace } from '../kube-resource.actions'; +import { + DeleteKubernetesResource, + GetKubernetesResource, + GetKubernetesResources, + GetKubernetesResourcesInNamespace, +} from '../kube-resource.actions'; +import { BasicKubeAPIResource } from '../kube.types'; export interface KubeResourceActionBuilders extends OrchestratedActionBuilders { @@ -16,6 +22,12 @@ export interface KubeResourceActionBuilders extends OrchestratedActionBuilders { kubeGuid: string, namespace: string ) => GetKubernetesResourcesInNamespace; + deleteResource: ( + resource: BasicKubeAPIResource, + kubeGuid: string, + resourceName: string, + namespace?: string + ) => DeleteKubernetesResource; } export function createKubeResourceActionBuilder(entityType: string): KubeResourceActionBuilders { @@ -23,5 +35,7 @@ export function createKubeResourceActionBuilder(entityType: string): KubeResourc get: (resName: string, kubeGuid: string, { namespace }) => new GetKubernetesResource(entityType, resName, namespace, kubeGuid), getMultiple: (kubeGuid: string, paginationKey?: string) => new GetKubernetesResources(entityType, kubeGuid), getInNamespace: (kubeGuid: string, namespace: string) => new GetKubernetesResourcesInNamespace(entityType, kubeGuid, namespace), + deleteResource: (resource: BasicKubeAPIResource, resName: string, kubeGuid: string, namespace: string) => + new DeleteKubernetesResource(entityType, resource, kubeGuid, resName, namespace) }; } diff --git a/src/frontend/packages/kubernetes/src/kubernetes/store/action-builders/kube.action-builders.ts b/src/frontend/packages/kubernetes/src/kubernetes/store/action-builders/kube.action-builders.ts index c5362463fd..c61e5d9ed8 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/store/action-builders/kube.action-builders.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/store/action-builders/kube.action-builders.ts @@ -1,4 +1,5 @@ import { OrchestratedActionBuilders } from '../../../../../store/src/entity-catalog/action-orchestrator/action-orchestrator'; +import { kubernetesNamespacesEntityType, kubernetesPodsEntityType } from '../../kubernetes-entity-factory'; import { GetHelmReleasePods, GetHelmReleaseServices } from '../../workloads/store/workloads.actions'; import { DeleteAnalysisReport, @@ -7,6 +8,8 @@ import { GetAnalysisReportsByPath, RunAnalysisReport, } from '../analysis.actions'; +import { DeleteKubernetesResource } from '../kube-resource.actions'; +import { BasicKubeAPIResource } from '../kube.types'; import { CreateKubernetesNamespace, GeKubernetesDeployments, @@ -58,6 +61,12 @@ export interface KubePodActionBuilders extends OrchestratedActionBuilders { kubeGuid: string, releaseTitle: string ) => GetHelmReleasePods; + deleteResource: ( + resource: BasicKubeAPIResource, + kubeGuid: string, + resourceName: string, + namespace?: string + ) => DeleteKubernetesResource; } export const kubePodActionBuilders: KubePodActionBuilders = { @@ -65,7 +74,13 @@ export const kubePodActionBuilders: KubePodActionBuilders = { getMultiple: (kubeGuid: string, paginationKey?: string) => new GetKubernetesPods(kubeGuid), getOnNode: (kubeGuid: string, nodeName: string) => new GetKubernetesPodsOnNode(kubeGuid, nodeName), getInNamespace: (kubeGuid: string, namespace: string) => new GetKubernetesPodsInNamespace(kubeGuid, namespace), - getInWorkload: (kubeGuid: string, releaseTitle: string) => new GetHelmReleasePods(kubeGuid, releaseTitle) + getInWorkload: (kubeGuid: string, releaseTitle: string) => new GetHelmReleasePods(kubeGuid, releaseTitle), + deleteResource: ( + resource: BasicKubeAPIResource, + kubeGuid: string, + resourceName: string, + namespace?: string + ) => new DeleteKubernetesResource(kubernetesPodsEntityType, resource, kubeGuid, resourceName, namespace) }; export interface KubeDeploymentActionBuilders extends OrchestratedActionBuilders { @@ -112,12 +127,20 @@ export interface KubeNamespaceActionBuilders extends OrchestratedActionBuilders kubeGuid: string, paginationKey?: string, ) => GetKubernetesNamespaces; + deleteResource: ( + resource: BasicKubeAPIResource, + kubeGuid: string, + resourceName: string, + namespace?: string + ) => DeleteKubernetesResource; } export const kubeNamespaceActionBuilders: KubeNamespaceActionBuilders = { get: (namespace: string, kubeGuid: string) => new GetKubernetesNamespace(namespace, kubeGuid), create: (namespace: string, kubeGuid: string) => new CreateKubernetesNamespace(namespace, kubeGuid), - getMultiple: (kubeGuid: string, paginationKey?: string) => new GetKubernetesNamespaces(kubeGuid) + getMultiple: (kubeGuid: string, paginationKey?: string) => new GetKubernetesNamespaces(kubeGuid), + deleteResource: (resource: BasicKubeAPIResource, kubeGuid: string, resName: string, namespace: string) => + new DeleteKubernetesResource(kubernetesNamespacesEntityType, resource, kubeGuid, resName, namespace) }; export interface KubeServiceActionBuilders extends OrchestratedActionBuilders { @@ -138,7 +161,7 @@ export interface KubeServiceActionBuilders extends OrchestratedActionBuilders { export const kubeServiceActionBuilders: KubeServiceActionBuilders = { getMultiple: (kubeGuid: string, paginationKey?: string) => new GetKubernetesServices(kubeGuid), getInNamespace: (kubeGuid: string, namespace: string) => new GetKubernetesServicesInNamespace(kubeGuid, namespace), - getInWorkload: (releaseTitle: string, kubeGuid: string) => new GetHelmReleaseServices(kubeGuid, releaseTitle) + getInWorkload: (releaseTitle: string, kubeGuid: string) => new GetHelmReleaseServices(kubeGuid, releaseTitle), }; export interface KubeDashboardActionBuilders extends OrchestratedActionBuilders { diff --git a/src/frontend/packages/kubernetes/src/kubernetes/store/kube-resource.actions.ts b/src/frontend/packages/kubernetes/src/kubernetes/store/kube-resource.actions.ts index c9a350270d..868335fb00 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/store/kube-resource.actions.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/store/kube-resource.actions.ts @@ -1,9 +1,12 @@ import { SortDirection } from '@angular/material/sort'; import { getPaginationKey } from '../../../../store/src/actions/pagination.actions'; +import { EntitySchema } from '../../../../store/src/helpers/entity-schema'; +import { ApiRequestTypes } from '../../../../store/src/reducers/api-request-reducer/request-helpers'; import { PaginationParam } from '../../../../store/src/types/pagination.types'; import { KUBERNETES_ENDPOINT_TYPE, kubernetesEntityFactory } from '../kubernetes-entity-factory'; import { getGuidFromKubePod } from './kube.getIds'; +import { BasicKubeAPIResource } from './kube.types'; import { KubePaginationAction, KubeSingleEntityAction } from './kubernetes.actions'; @@ -19,6 +22,9 @@ export const GET_KUBE_RESOURCES_IN_NAMESPACE = '[KUBERNETES Endpoint] Get Resour export const GET_KUBE_RESOURCES_IN_NAMESPACE_SUCCESS = '[KUBERNETES Endpoint] Get Resources in namespace Success'; export const GET_KUBE_RESOURCES_IN_NAMESPACE_FAILURE = '[KUBERNETES Endpoint] Get Resources in namespace Failure'; +export const DELETE_KUBE_RESOURCE = '[KUBERNETES Endpoint] Delete Resource'; +export const DELETE_KUBE_RESOURCE_SUCCESS = '[KUBERNETES Endpoint] Delete Resource Success'; +export const DELETE_KUBE_RESOURCE_FAILURE = '[KUBERNETES Endpoint] Delete Resource Failure'; const defaultSortParams = { 'order-direction': 'desc' as SortDirection, @@ -78,3 +84,30 @@ export class GetKubernetesResourcesInNamespace extends GetKubernetesResources { GET_KUBE_RESOURCES_IN_NAMESPACE_FAILURE ]; } + +export class DeleteKubernetesResource implements KubeSingleEntityAction { + + public entity: EntitySchema[]; + + constructor( + public entityType: string, + private resource: BasicKubeAPIResource, + public kubeGuid: string, + public name: string, + public namespace?: string + ) { + const schema = kubernetesEntityFactory(this.entityType); + this.entity = [schema]; + this.guid = schema.getId(resource); + } + + type = DELETE_KUBE_RESOURCE; + endpointType = KUBERNETES_ENDPOINT_TYPE; + actions = [ + DELETE_KUBE_RESOURCE, + DELETE_KUBE_RESOURCE_SUCCESS, + DELETE_KUBE_RESOURCE_FAILURE + ]; + requestType: ApiRequestTypes = 'delete'; + guid: string; +} diff --git a/src/frontend/packages/kubernetes/src/kubernetes/store/kube.types.ts b/src/frontend/packages/kubernetes/src/kubernetes/store/kube.types.ts index 708264ec46..602e67f8a7 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/store/kube.types.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/store/kube.types.ts @@ -6,6 +6,7 @@ import { } from '../../../../store/src/entity-catalog/action-orchestrator/action-orchestrator'; import { StratosCatalogEntity } from '../../../../store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity'; import { IEntityMetadata, IStratosEntityDefinition } from '../../../../store/src/entity-catalog/entity-catalog.types'; +import { UserFavorite } from '../../../../store/src/types/user-favorites.types'; import { KubernetesPodExpandedStatus } from '../services/kubernetes-expanded-state'; // Map of endpoint ID to current namespace for that endpoint @@ -57,11 +58,14 @@ export interface KubeResourceEntityDefinition< iconFont?: string; type: string; getKubeCatalogEntity?: (IStratosEntityDefinition) => StratosCatalogEntity; + getIsValid?: (fav: UserFavorite) => Observable; listColumns?: SimpleKubeListColumn[]; // Should this entity be hidden in the auto-generated navigation? hidden?: boolean; // Name fo a list config that can be obtained from the list config service listConfig?: string; + // Allow this entity to be favorited? + canFavorite?: boolean; } export interface KubeService extends BasicKubeAPIResource { diff --git a/src/frontend/packages/kubernetes/src/kubernetes/store/kubernetes.actions.ts b/src/frontend/packages/kubernetes/src/kubernetes/store/kubernetes.actions.ts index f808c25fb9..6135e3150a 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/store/kubernetes.actions.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/store/kubernetes.actions.ts @@ -100,6 +100,7 @@ export interface KubeAction extends EntityRequestAction { } export interface KubePaginationAction extends PaginatedAction, KubeAction { flattenPagination: boolean; + continuationToken?: string; } export interface KubeSingleEntityAction extends KubeAction { guid: string; diff --git a/src/frontend/packages/kubernetes/src/kubernetes/store/kubernetes.effects.ts b/src/frontend/packages/kubernetes/src/kubernetes/store/kubernetes.effects.ts index b112159622..ee88fc90fa 100644 --- a/src/frontend/packages/kubernetes/src/kubernetes/store/kubernetes.effects.ts +++ b/src/frontend/packages/kubernetes/src/kubernetes/store/kubernetes.effects.ts @@ -2,7 +2,7 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Actions, Effect, ofType } from '@ngrx/effects'; import { Action, Store } from '@ngrx/store'; -import { ClearPaginationOfType } from 'frontend/packages/store/src/actions/pagination.actions'; +import { ClearPaginationOfEntity, ClearPaginationOfType } from 'frontend/packages/store/src/actions/pagination.actions'; import { ApiRequestTypes } from 'frontend/packages/store/src/reducers/api-request-reducer/request-helpers'; import { connectedEndpointsOfTypesSelector } from 'frontend/packages/store/src/selectors/endpoint.selectors'; import { of } from 'rxjs'; @@ -19,6 +19,8 @@ import { } from '../kubernetes-entity-factory'; import { KubernetesPodExpandedStatusHelper } from '../services/kubernetes-expanded-state'; import { + DELETE_KUBE_RESOURCE, + DeleteKubernetesResource, GET_KUBE_RESOURCES, GET_KUBE_RESOURCES_IN_NAMESPACE, GetKubernetesResources, @@ -312,6 +314,28 @@ export class KubernetesEffects { // ======================================================================================= + @Effect() + deleteKubeResource$ = this.actions$.pipe( + ofType(DELETE_KUBE_RESOURCE), + flatMap(action => { + const catalog = entityCatalog.getEntity(action.endpointType, action.entityType); + if (catalog && catalog.definition) { + const defn = catalog.definition as IKubeResourceEntityDefinition; + if (defn.apiVersion && defn.apiName) { + let apiURL; + if (action.namespace) { + apiURL = `/pp/${this.proxyAPIVersion}/proxy${defn.apiVersion}/namespaces/${action.namespace}/${defn.apiName}/${action.name}`; + } else { + apiURL = `/pp/${this.proxyAPIVersion}/proxy${defn.apiVersion}/${defn.apiName}/${action.name}`; + } + return this.processSingleItemDeleteAction(action, apiURL); + } + } + }) + ); + + // ======================================================================================= + private processNodeAction(action: GetKubernetesNodes) { return this.processListAction( action, @@ -324,6 +348,11 @@ export class KubernetesEffects { url: string) { this.store.dispatch(new StartRequestAction(action)); + let limit = `${url}?limit=500`; + if (action.continuationToken) { + limit = `${limit}&continue=${action.continuationToken}`; + } + const getKubeIds = action.kubeGuid ? of([action.kubeGuid]) : this.store.select(connectedEndpointsOfTypesSelector(KUBERNETES_ENDPOINT_TYPE)).pipe( @@ -347,7 +376,7 @@ export class KubernetesEffects { return httpParams.set(initialKey, paginationAction.initialParams[initialKey].toString()); }, new HttpParams()); } - return this.http.get(url, requestArgs); + return this.http.get(limit, requestArgs); }), mergeMap(allRes => { const base = { @@ -377,6 +406,21 @@ export class KubernetesEffects { res.result.push(id); return res; }, base); + + // Can we de-paginate like this? (Note: We only really support a single endpoint) + // Object.entries(allRes).forEach(([kubeId, res]) => { + // console.log(res); + // if (res.metadata.continue) { + // console.log('continue for ' + kubeId); + // action.continuationToken = res.metadata.continue; + + // const nextPageAction = {...action}; + // nextPageAction.continuationToken = res.metadata.continue; + // nextPageAction.pageNumber = action.pageNumber ? action.pageNumber + 1 : 2; + // this.store.dispatch(nextPageAction); + // } + // }); + return [ new WrapperRequestActionSuccess(processesData, action) ]; @@ -447,6 +491,50 @@ export class KubernetesEffects { ); } + private processSingleItemDeleteAction(action: KubeAction, url: string) { + this.store.dispatch(new StartRequestAction(action, 'delete')); + const headers = new HttpHeaders({ + 'x-cap-cnsi-list': action.kubeGuid, + 'x-cap-passthrough': 'true' + }, + ); + const requestArgs = { + headers + }; + const request = this.http.delete(url, requestArgs); + const entityKey = entityCatalog.getEntityKey(action); + return request + .pipe( + mergeMap((response: T) => { + const res = { + entities: { [entityKey]: {} }, + result: [] + } as NormalizedResponse; + response.metadata.kubeId = action.kubeGuid; + res.entities[entityKey][action.guid] = response; + res.result.push(action.guid); + const actions: Action[] = [ + new WrapperRequestActionSuccess(res, action) + ]; + // actions.push(new ClearPaginationOfType(action)); + actions.push(new ClearPaginationOfEntity(action, action.guid)); + return actions; + }), + catchError(error => { + const { status, message } = this.createKubeError(error); + return [ + new WrapperRequestActionFailed(message, action, 'delete', { + endpointIds: [action.kubeGuid], + url: error.url || url, + eventCode: status, + message, + error + }) + ]; + }) + ); + } + private createKubeErrorMessage(err: any): string { if (err) { if (err.error && err.error.message) { diff --git a/src/frontend/packages/store/src/operators.ts b/src/frontend/packages/store/src/operators.ts index 21ccb2a7a2..8f886d025a 100644 --- a/src/frontend/packages/store/src/operators.ts +++ b/src/frontend/packages/store/src/operators.ts @@ -1,6 +1,8 @@ import { OperatorFunction } from 'rxjs'; import { filter, map, pairwise } from 'rxjs/operators'; +import { DeleteActionState, RequestInfoState } from './reducers/api-request-reducer/types'; + // Helper operators // Monitors an entity fetch operation and generates a single boolean to @@ -13,3 +15,25 @@ export function entityFetchedWithoutError(): OperatorFunction { map(f => !(f as any).error) ); } + +// Monitors an entity delete operation and generates a single boolean to +// indicate if the delete succeeded without an error +export function entityDeletedWithoutError(): OperatorFunction { + return input$ => input$.pipe( + map((status: RequestInfoState) => status.deleting), + pairwise(), + filter(([oldV, newV]) => (oldV as any).busy && !(newV as any).busy), + map(([, newV]) => newV), + map(f => !(f as any).error) + ); +} + +// Monitors an entity delete operation +export function entityDeleted(): OperatorFunction { + return input$ => input$.pipe( + map((status: RequestInfoState) => status.deleting), + pairwise(), + filter(([oldV, newV]) => (oldV as any).busy && !(newV as any).busy), + map(([, newV]) => newV), + ); +} diff --git a/src/frontend/packages/store/src/user-favorite-manager.ts b/src/frontend/packages/store/src/user-favorite-manager.ts index befb073c0f..dd88e2e73c 100644 --- a/src/frontend/packages/store/src/user-favorite-manager.ts +++ b/src/frontend/packages/store/src/user-favorite-manager.ts @@ -227,4 +227,12 @@ export class UserFavoriteManager { return total > 0; } + public canFavoriteEntityType(entityDefn: StratosBaseCatalogEntity) { + const defn = entityDefn.builders?.entityBuilder; + if (defn) { + const canFavorite = defn.getGuid && defn.getMetadata && defn.getLink; + return canFavorite; + } + return false; + } } diff --git a/src/jetstream/go.sum b/src/jetstream/go.sum index 9dd7469114..4fad84b7c4 100644 --- a/src/jetstream/go.sum +++ b/src/jetstream/go.sum @@ -802,6 +802,7 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= helm.sh/helm/v3 v3.0.0 h1:or/9cs1GgfcTQeEnR2CVJNw893/rmqIG1KsNHmUiSFw=