Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typed access to actions and entities via catalog entity #377

Merged
merged 22 commits into from
Jun 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,19 @@ import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/co
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MatTextareaAutosize } from '@angular/material/input';
import { ActivatedRoute } from '@angular/router';
import { Store } from '@ngrx/store';
import { PaginationMonitorFactory } from 'frontend/packages/store/src/monitors/pagination-monitor.factory';
import { getPaginationObservables } from 'frontend/packages/store/src/reducers/pagination-reducer/pagination-reducer.helper';
import { BehaviorSubject, combineLatest, Observable, of, Subscription } from 'rxjs';
import { distinctUntilChanged, filter, first, map, pairwise, startWith, switchMap } from 'rxjs/operators';

import { AppState } from '../../../../../store/src/app-state';
import { EntityMonitorFactory } from '../../../../../store/src/monitors/entity-monitor.factory.service';
import { RequestInfoState } from '../../../../../store/src/reducers/api-request-reducer/types';
import { EndpointsService } from '../../../core/endpoints.service';
import { safeUnsubscribe } from '../../../core/utils.service';
import { ConfirmationDialogConfig } from '../../../shared/components/confirmation-dialog.config';
import { ConfirmationDialogService } from '../../../shared/components/confirmation-dialog.service';
import { StepOnNextFunction, StepOnNextResult } from '../../../shared/components/stepper/step/step.component';
import { kubeEntityCatalog } from '../../kubernetes/kubernetes-entity-catalog';
import { KUBERNETES_ENDPOINT_TYPE } from '../../kubernetes/kubernetes-entity-factory';
import { KubernetesNamespace } from '../../kubernetes/store/kube.types';
import { CreateKubernetesNamespace, GetKubernetesNamespaces } from '../../kubernetes/store/kubernetes.actions';
import { HelmInstall } from '../store/helm.actions';
import { helmEntityCatalog } from '../helm-entity-catalog';
import { HelmInstallValues } from '../store/helm.types';

@Component({
Expand Down Expand Up @@ -61,11 +57,8 @@ export class CreateReleaseComponent implements OnInit, OnDestroy {
constructor(
private route: ActivatedRoute,
public endpointsService: EndpointsService,
private store: Store<AppState>,
private httpClient: HttpClient,
private confirmDialog: ConfirmationDialogService,
private pmf: PaginationMonitorFactory,
private emf: EntityMonitorFactory
) {
const chart = this.route.snapshot.params;
this.cancelUrl = `/monocular/charts/${chart.repo}/${chart.chartName}/${chart.version}`;
Expand Down Expand Up @@ -98,12 +91,7 @@ export class CreateReleaseComponent implements OnInit, OnDestroy {

this.kubeEndpoints$ = this.endpointsService.connectedEndpointsOfTypes(KUBERNETES_ENDPOINT_TYPE);

const action = new GetKubernetesNamespaces(null);
const allNamespaces$ = getPaginationObservables<KubernetesNamespace>({
store: this.store,
action,
paginationMonitor: this.pmf.create(action.paginationKey, action, true)
}).entities$.pipe(
const allNamespaces$ = kubeEntityCatalog.namespace.store.getPaginationService(null).entities$.pipe(
richard-cox marked this conversation as resolved.
Show resolved Hide resolved
filter(namespaces => !!namespaces),
first()
);
Expand Down Expand Up @@ -234,12 +222,10 @@ export class CreateReleaseComponent implements OnInit, OnDestroy {
});
}

const action = new CreateKubernetesNamespace(
return kubeEntityCatalog.namespace.api.create<RequestInfoState>(
this.details.controls.releaseNamespace.value,
this.details.controls.endpoint.value);
this.store.dispatch(action);

return this.emf.create(action.guid, action).entityRequest$.pipe(
this.details.controls.endpoint.value
).pipe(
pairwise(),
filter(([oldVal, newVal]) => oldVal.creating && !newVal.creating),
map(([, newVal]) => newVal),
Expand Down Expand Up @@ -267,11 +253,8 @@ export class CreateReleaseComponent implements OnInit, OnDestroy {
};

// Make the request
const action = new HelmInstall(values);
this.store.dispatch(action);

// Wait for result of request
return this.emf.create(action.guid, action).entityRequest$.pipe(
return helmEntityCatalog.chart.api.install<RequestInfoState>(values).pipe(
// Wait for result of request
filter(state => !!state),
pairwise(),
filter(([oldVal, newVal]) => (oldVal.creating && !newVal.creating)),
Expand Down
17 changes: 17 additions & 0 deletions custom-src/frontend/app/custom/helm/helm-entity-catalog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { StratosCatalogEndpointEntity, StratosCatalogEntity } from '../../../../store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity'; import { IFavoriteMetadata } from '../../../../store/src/types/user-favorites.types'; import { MonocularChart, HelmVersion } from './store/helm.types'; import { HelmChartActionBuilders, HelmVersionActionBuilders } from './store/helm.action-builders';

/**
* A strongly typed collection of Helm Catalog Entities.
* This can be used to access functionality exposed by each specific type, such as get, update, delete, etc
*/
export class HelmEntityCatalog {
endpoint: StratosCatalogEndpointEntity;
chart: StratosCatalogEntity<IFavoriteMetadata, MonocularChart, HelmChartActionBuilders>
version: StratosCatalogEntity<IFavoriteMetadata, HelmVersion, HelmVersionActionBuilders>
}

/**
* A strongly typed collection of Helm Catalog Entities.
* This can be used to access functionality exposed by each specific type, such as get, update, delete, etc
*/
export const helmEntityCatalog: HelmEntityCatalog = new HelmEntityCatalog();
26 changes: 23 additions & 3 deletions custom-src/frontend/app/custom/helm/helm-entity-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@ import {
} from '../../../../store/src/entity-catalog/entity-catalog-entity/entity-catalog-entity';
import { StratosEndpointExtensionDefinition } from '../../../../store/src/entity-catalog/entity-catalog.types';
import { IFavoriteMetadata } from '../../../../store/src/types/user-favorites.types';
import { helmEntityCatalog } from './helm-entity-catalog';
import {
HELM_ENDPOINT_TYPE,
helmEntityFactory,
helmVersionsEntityType,
monocularChartsEntityType,
} from './helm-entity-factory';
import {
HelmChartActionBuilders,
helmChartActionBuilders,
HelmVersionActionBuilders,
helmVersionActionBuilders,
} from './store/helm.action-builders';
import { HelmVersion, MonocularChart } from './store/helm.types';


Expand All @@ -36,10 +43,11 @@ export function generateHelmEntities(): StratosBaseCatalogEntity[] {
}

function generateEndpointEntity(endpointDefinition: StratosEndpointExtensionDefinition) {
return new StratosCatalogEndpointEntity(
helmEntityCatalog.endpoint = new StratosCatalogEndpointEntity(
endpointDefinition,
metadata => `/monocular/repos/${metadata.guid}`,
);
return helmEntityCatalog.endpoint;
}

function generateChartEntity(endpointDefinition: StratosEndpointExtensionDefinition) {
Expand All @@ -48,7 +56,13 @@ function generateChartEntity(endpointDefinition: StratosEndpointExtensionDefinit
schema: helmEntityFactory(monocularChartsEntityType),
endpoint: endpointDefinition
};
return new StratosCatalogEntity<IFavoriteMetadata, MonocularChart>(definition);
helmEntityCatalog.chart = new StratosCatalogEntity<IFavoriteMetadata, MonocularChart, HelmChartActionBuilders>(
definition,
{
actionBuilders: helmChartActionBuilders
}
);
return helmEntityCatalog.chart;
}

function generateVersionEntity(endpointDefinition: StratosEndpointExtensionDefinition) {
Expand All @@ -57,7 +71,13 @@ function generateVersionEntity(endpointDefinition: StratosEndpointExtensionDefin
schema: helmEntityFactory(helmVersionsEntityType),
endpoint: endpointDefinition
};
return new StratosCatalogEntity<IFavoriteMetadata, HelmVersion>(definition);
helmEntityCatalog.version = new StratosCatalogEntity<IFavoriteMetadata, HelmVersion, HelmVersionActionBuilders>(
definition,
{
actionBuilders: helmVersionActionBuilders
}
);
return helmEntityCatalog.version;
}


3 changes: 3 additions & 0 deletions custom-src/frontend/app/custom/helm/helm-testing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ import { RouterTestingModule } from '@angular/router/testing';
import { CATALOGUE_ENTITIES, EntityCatalogFeatureModule } from '../../../../store/src/entity-catalog.module';
import { entityCatalog, TestEntityCatalog } from '../../../../store/src/entity-catalog/entity-catalog';
import { createBasicStoreModule } from '../../../../store/testing/public-api';
import { AppTestModule } from '../../../test-framework/core-test.helper';
import { generateStratosEntities } from '../../base-entity-types';
import { CoreModule } from '../../core/core.module';
import { SharedModule } from '../../shared/shared.module';
import { HelmReleaseGuid } from '../kubernetes/workloads/workload.types';
import { generateHelmEntities } from './helm-entity-generator';


@NgModule({
imports: [{
ngModule: EntityCatalogFeatureModule,
Expand Down Expand Up @@ -53,6 +55,7 @@ export const HelmReleaseGuidMock = {
};

export const HelmBaseTestModules = [
AppTestModule,
HelmTestingModule,
RouterTestingModule,
CoreModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import { PaginationEntityState } from '../../../../../store/src//types/paginatio
import { AppState } from '../../../../../store/src/app-state';
import { ListDataSource } from '../../../shared/components/list/data-sources-controllers/list-data-source';
import { IListConfig } from '../../../shared/components/list/list.component.types';
import { getMonocularChartId, helmEntityFactory, monocularChartsEntityType } from '../helm-entity-factory';
import { GetMonocularCharts } from '../store/helm.actions';
import { helmEntityCatalog } from '../helm-entity-catalog';
import { MonocularChart } from '../store/helm.types';

export class MonocularChartsDataSource extends ListDataSource<MonocularChart> {
Expand All @@ -14,12 +13,12 @@ export class MonocularChartsDataSource extends ListDataSource<MonocularChart> {
store: Store<AppState>,
listConfig: IListConfig<MonocularChart>
) {
const action = new GetMonocularCharts();
const action = helmEntityCatalog.chart.actions.getMultiple();
super({
store,
action,
schema: helmEntityFactory(monocularChartsEntityType),
getRowUniqueId: getMonocularChartId,
schema: action.entity[0],
getRowUniqueId: (row) => action.entity[0].getId(row),
paginationKey: action.paginationKey,
isLocal: true,
listConfig,
Expand Down
23 changes: 23 additions & 0 deletions custom-src/frontend/app/custom/helm/store/helm.action-builders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { OrchestratedActionBuilders } from '../../../../../store/src/entity-catalog/action-orchestrator/action-orchestrator';
import { GetHelmVersions, GetMonocularCharts, HelmInstall } from './helm.actions';
import { HelmInstallValues } from './helm.types';

export interface HelmChartActionBuilders extends OrchestratedActionBuilders {
getMultiple: () => GetMonocularCharts,
// Helm install added to chart action builder and not helm release/workload to ensure action & effect are available in this module
// (others may not have loaded)
install: (values: HelmInstallValues) => HelmInstall
}

export const helmChartActionBuilders: HelmChartActionBuilders = {
getMultiple: () => new GetMonocularCharts(),
install: (values: HelmInstallValues) => new HelmInstall(values)
}

export interface HelmVersionActionBuilders extends OrchestratedActionBuilders {
getMultiple: () => GetHelmVersions
}

export const helmVersionActionBuilders: HelmVersionActionBuilders = {
getMultiple: () => new GetHelmVersions()
}
24 changes: 11 additions & 13 deletions custom-src/frontend/app/custom/helm/store/helm.actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { EntityRequestAction } from '../../../../../store/src/types/request.types';
import { KUBERNETES_ENDPOINT_TYPE } from '../../kubernetes/kubernetes-entity-factory';
import { helmReleaseEntityKey } from '../../kubernetes/workloads/store/workloads-entity-factory';
import {
HELM_ENDPOINT_TYPE,
helmEntityFactory,
Expand Down Expand Up @@ -42,8 +40,18 @@ export class GetMonocularCharts implements MonocularPaginationAction {
'order-direction': 'desc',
'order-direction-field': 'name',
};
flattenPagination = true;
}

export class HelmInstall implements EntityRequestAction {
type = HELM_INSTALL;
endpointType = HELM_ENDPOINT_TYPE;
entityType = monocularChartsEntityType;
guid: string;
constructor(public values: HelmInstallValues) {
this.guid = '<New Release>' + this.values.releaseName;
}
}

export class GetHelmVersions implements MonocularPaginationAction {
constructor() {
Expand All @@ -63,15 +71,5 @@ export class GetHelmVersions implements MonocularPaginationAction {
'order-direction': 'asc',
'order-direction-field': 'version',
};
flattenPagination = true;
}

export class HelmInstall implements EntityRequestAction {
type = HELM_INSTALL;
endpointType = KUBERNETES_ENDPOINT_TYPE;
entityType = helmReleaseEntityKey;
guid: string;
constructor(public values: HelmInstallValues) {
this.guid = '<New Release>' + this.values.releaseName;
}
}

39 changes: 20 additions & 19 deletions custom-src/frontend/app/custom/helm/store/helm.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@ import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { Action, Store } from '@ngrx/store';
import { GET_ENDPOINTS_SUCCESS, GetAllEndpointsSuccess } from 'frontend/packages/store/src/actions/endpoint.actions';
import { ClearPaginationOfType } from 'frontend/packages/store/src/actions/pagination.actions';
import { AppState } from 'frontend/packages/store/src/app-state';
import { entityCatalog } from 'frontend/packages/store/src/entity-catalog/entity-catalog';
import { ApiRequestTypes } from 'frontend/packages/store/src/reducers/api-request-reducer/request-helpers';
import { NormalizedResponse } from 'frontend/packages/store/src/types/api.types';
import { Observable } from 'rxjs';
import { catchError, flatMap, mergeMap } from 'rxjs/operators';

import { GET_ENDPOINTS_SUCCESS, GetAllEndpointsSuccess } from '../../../../../store/src/actions/endpoint.actions';
import { ClearPaginationOfType } from '../../../../../store/src/actions/pagination.actions';
import { AppState } from '../../../../../store/src/app-state';
import { entityCatalog } from '../../../../../store/src/entity-catalog/entity-catalog';
import { ApiRequestTypes } from '../../../../../store/src/reducers/api-request-reducer/request-helpers';
import { NormalizedResponse } from '../../../../../store/src/types/api.types';
import {
EntityRequestAction,
StartRequestAction,
WrapperRequestActionFailed,
WrapperRequestActionSuccess,
} from 'frontend/packages/store/src/types/request.types';
import { Observable } from 'rxjs';
import { catchError, flatMap, mergeMap } from 'rxjs/operators';

} from '../../../../../store/src/types/request.types';
import { environment } from '../../../environments/environment';
import { isJetstreamError } from '../../../jetstream.helpers';
import { HELM_ENDPOINT_TYPE } from '../helm-entity-factory';
import { helmEntityCatalog } from '../helm-entity-catalog';
import { getHelmVersionId, getMonocularChartId, HELM_ENDPOINT_TYPE } from '../helm-entity-factory';
import {
GET_HELM_VERSIONS,
GET_MONOCULAR_CHARTS,
Expand Down Expand Up @@ -85,7 +86,7 @@ export class HelmEffects {

const items = response.data as Array<any>;
const processedData = items.reduce((res, data) => {
const id = data.id;
const id = getMonocularChartId(data);
res.entities[entityKey][id] = data;
// Promote the name to the top-level object for simplicity
data.name = data.attributes.name;
Expand Down Expand Up @@ -119,7 +120,7 @@ export class HelmEffects {
endpointId: endpoint,
...endpointData
};
processedData.entities[entityKey][endpoint] = version;
processedData.entities[entityKey][getHelmVersionId(version)] = version;
processedData.result.push(endpoint);
});
return processedData;
Expand All @@ -142,7 +143,7 @@ export class HelmEffects {
];
}),
catchError(error => {
const { status, message } = this.createHelmError(error);
const { status, message } = HelmEffects.createHelmError(error);
const errorMessage = `Failed to install helm chart: ${message}`;
return [
new WrapperRequestActionFailed(errorMessage, action, requestType, {
Expand Down Expand Up @@ -172,7 +173,7 @@ export class HelmEffects {
return this.httpClient.get(url, requestArgs).pipe(
mergeMap((response: any) => [new WrapperRequestActionSuccess(mapResult(response), action)]),
catchError(error => {
const { status, message } = this.createHelmError(error);
const { status, message } = HelmEffects.createHelmError(error);
return [
new WrapperRequestActionFailed(message, action, 'fetch', {
endpointIds,
Expand All @@ -186,7 +187,7 @@ export class HelmEffects {
);
}

private createHelmErrorMessage(err: any): string {
static createHelmErrorMessage(err: any): string {
if (err) {
if (err.error && err.error.message) {
// Kube error
Expand All @@ -202,7 +203,7 @@ export class HelmEffects {
return 'Helm API request error';
}

private createHelmError(err: any): { status: string, message: string } {
static createHelmError(err: any): { status: string, message: string } {
let unwrapped = err;
if (err.error) {
unwrapped = err.error;
Expand All @@ -212,7 +213,7 @@ export class HelmEffects {
// Wrapped error
return {
status: jetstreamError.error.statusCode.toString(),
message: this.createHelmErrorMessage(jetstreamError)
message: HelmEffects.createHelmErrorMessage(jetstreamError)
};
}
return {
Expand Down Expand Up @@ -242,7 +243,7 @@ export class HelmEffects {
this.syncing = syncing;
if (remaining !== existing) {
// Dispatch action to refresh charts
this.store.dispatch(new GetMonocularCharts());
helmEntityCatalog.chart.api.getMultiple();
}
if (remaining > 0) {
this.scheduleSyncStatusCheck();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

&__content {
& > * {
padding-bottom: 10px;
margin-bottom: 10px;
}
textarea {
max-height: 75px;
Expand Down
Loading