Skip to content

Commit

Permalink
Merge pull request #3292 from cloudfoundry-incubator/cf-routes-tab
Browse files Browse the repository at this point in the history
Add a routes list to the CF tabs, Routes Refactor & Route Bug Fixes
  • Loading branch information
nwmac authored Jan 10, 2019
2 parents fe147ba + 11e6468 commit f134307
Show file tree
Hide file tree
Showing 72 changed files with 1,228 additions and 1,039 deletions.
13 changes: 12 additions & 1 deletion src/frontend/app/core/cf-api.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ export interface IRoute {
cfGuid?: string;
}

/**
* Different routes interface to cover the non-standard entity returned from a app summary request
*/
export interface IAppSummaryRoute {
domain: IDomain;
guid: string;
host: string;
path: string;
port?: any;
}

export interface ISpace {
name: string;
organization_guid: string;
Expand Down Expand Up @@ -265,7 +276,7 @@ export interface IUpdateOrganization {
export interface IAppSummary {
guid: string;
name: string;
routes: APIResource<IRoute>[];
routes: APIResource<IAppSummaryRoute>[];
running_instances: number;
services: IService[];
available_domains: IDomain[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import {
} from '../../../shared/components/list/list-types/app-route/cf-app-routes-list-config.service';
import {
TableCellRouteComponent,
} from '../../../shared/components/list/list-types/app-route/table-cell-route/table-cell-route.component';
} from '../../../shared/components/list/list-types/cf-routes/table-cell-route/table-cell-route.component';
import {
TableCellTCPRouteComponent,
} from '../../../shared/components/list/list-types/app-route/table-cell-tcproute/table-cell-tcproute.component';
} from '../../../shared/components/list/list-types/cf-routes/table-cell-tcproute/table-cell-tcproute.component';
import {
AppServiceBindingDataSource,
} from '../../../shared/components/list/list-types/app-sevice-bindings/app-service-binding-data-source';
Expand All @@ -37,7 +37,7 @@ import { EntityMonitorFactory } from '../../../shared/monitors/entity-monitor.fa
import { PaginationMonitor } from '../../../shared/monitors/pagination-monitor';
import { PaginationMonitorFactory } from '../../../shared/monitors/pagination-monitor.factory';
import { GetAppRoutes } from '../../../store/actions/application-service-routes.actions';
import { DeleteApplication, GetAllApplications, GetApplication } from '../../../store/actions/application.actions';
import { DeleteApplication, GetApplication } from '../../../store/actions/application.actions';
import { DeleteRoute } from '../../../store/actions/route.actions';
import { RouterNav } from '../../../store/actions/router.actions';
import { DeleteServiceInstance } from '../../../store/actions/service-instances.actions';
Expand All @@ -46,10 +46,8 @@ import {
applicationSchemaKey,
entityFactory,
routeSchemaKey,
serviceBindingSchemaKey,
serviceInstancesSchemaKey,
} from '../../../store/helpers/entity-factory';
import { createEntityRelationKey } from '../../../store/helpers/entity-relations/entity-relations.types';
import { APIResource } from '../../../store/types/api.types';
import { ApplicationService } from '../application.service';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,37 @@
import { DatePipe } from '@angular/common';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable, of as observableOf } from 'rxjs';

import { IRoute } from '../../../../core/cf-api.types';
import { CurrentUserPermissionsService } from '../../../../core/current-user-permissions.service';
import { ConfirmationDialogService } from '../../../../shared/components/confirmation-dialog.service';
import { RowState } from '../../../../shared/components/list/data-sources-controllers/list-data-source-types';
import {
CfAppRoutesListConfigService,
} from '../../../../shared/components/list/list-types/app-route/cf-app-routes-list-config.service';
import { ListView } from '../../../../store/actions/list.actions';
CfAppRoutesListConfigServiceBase,
} from '../../../../shared/components/list/list-types/app-route/cf-app-routes-list-config-base';
import { IListConfig } from '../../../../shared/components/list/list.component.types';
import { AppState } from '../../../../store/app-state';
import { ApplicationService } from '../../application.service';
import { IRoute } from '../../../../core/cf-api.types';
import { APIResource } from '../../../../store/types/api.types';
import { RowState } from '../../../../shared/components/list/data-sources-controllers/list-data-source-types';
import { Observable, of as observableOf } from 'rxjs';
import { GetAppRoutes } from '../../../../store/actions/application-service-routes.actions';
import { createEntityRelationKey } from '../../../../store/helpers/entity-relations/entity-relations.types';
import { routeSchemaKey, domainSchemaKey, applicationSchemaKey } from '../../../../store/helpers/entity-factory';
import { ApplicationService } from '../../application.service';


@Injectable()
export class AppDeleteRoutesListConfigService extends CfAppRoutesListConfigService {
defaultView: ListView;
export class AppDeleteRoutesListConfigService extends CfAppRoutesListConfigServiceBase implements IListConfig<APIResource> {
constructor(
store: Store<AppState>,
appService: ApplicationService,
confirmDialog: ConfirmationDialogService
confirmDialog: ConfirmationDialogService,
datePipe: DatePipe,
currentUserPermissionsService: CurrentUserPermissionsService,
) {
super(store, appService, confirmDialog, new GetAppRoutes(appService.appGuid, appService.cfGuid, null, [
createEntityRelationKey(routeSchemaKey, domainSchemaKey),
createEntityRelationKey(routeSchemaKey, applicationSchemaKey),
]));
this.getGlobalActions = () => null;
this.getMultiActions = () => null;
this.getSingleActions = () => null;
this.allowSelection = true;
this.routesDataSource.getRowState = (route: APIResource<IRoute>): Observable<RowState> =>
super(store, appService, confirmDialog, datePipe, currentUserPermissionsService, null, false, false);

this.setupList();
}

private setupList() {
this.getDataSource().getRowState = (route: APIResource<IRoute>): Observable<RowState> =>
observableOf({
disabledReason: 'Route is attached to other applications',
disabled: route && route.entity.apps ? route.entity.apps.length > 1 : false
Expand Down
40 changes: 16 additions & 24 deletions src/frontend/app/features/applications/application.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { combineLatest, filter, first, map, publishReplay, refCount, startWith, switchMap } from 'rxjs/operators';

import { IApp, IOrganization, ISpace } from '../../core/cf-api.types';
import { IApp, IOrganization, ISpace, IAppSummary } from '../../core/cf-api.types';
import { EntityService } from '../../core/entity-service';
import { EntityServiceFactory } from '../../core/entity-service-factory.service';
import {
Expand Down Expand Up @@ -36,7 +36,7 @@ import { ActionState, rootUpdatingKey } from '../../store/reducers/api-request-r
import { selectEntity, selectUpdateInfo } from '../../store/selectors/api.selectors';
import { endpointEntitiesSelector } from '../../store/selectors/endpoint.selectors';
import { APIResource, EntityInfo } from '../../store/types/api.types';
import { AppStat, AppSummary } from '../../store/types/app-metadata.types';
import { AppStat } from '../../store/types/app-metadata.types';
import { PaginationEntityState } from '../../store/types/pagination.types';
import {
getCurrentPageRequestInfo,
Expand Down Expand Up @@ -75,12 +75,12 @@ export interface ApplicationData {
@Injectable()
export class ApplicationService {

private appEntityService: EntityService;
private appSummaryEntityService: EntityService;
private appEntityService: EntityService<APIResource<IApp>>;
private appSummaryEntityService: EntityService<APIResource<IAppSummary>>;

constructor(
@Inject(CF_GUID) public cfGuid,
@Inject(APP_GUID) public appGuid,
@Inject(CF_GUID) public cfGuid: string,
@Inject(APP_GUID) public appGuid: string,
private store: Store<AppState>,
private entityServiceFactory: EntityServiceFactory,
private appStateService: ApplicationStateService,
Expand All @@ -95,7 +95,7 @@ export class ApplicationService {
createGetApplicationAction(appGuid, cfGuid)
);

this.appSummaryEntityService = this.entityServiceFactory.create(
this.appSummaryEntityService = this.entityServiceFactory.create<APIResource<IAppSummary>>(
appSummarySchemaKey,
entityFactory(appSummarySchemaKey),
appGuid,
Expand Down Expand Up @@ -123,7 +123,7 @@ export class ApplicationService {

app$: Observable<EntityInfo<APIResource<IApp>>>;
waitForAppEntity$: Observable<EntityInfo<APIResource<IApp>>>;
appSummary$: Observable<EntityInfo<AppSummary>>;
appSummary$: Observable<EntityInfo<APIResource<IAppSummary>>>;
appStats$: Observable<APIResource<AppStat>[]>;
private appStatsFetching$: Observable<PaginationEntityState>; // Use isFetchingStats$ which is properly gated
appEnvVars: PaginationObservables<APIResource>;
Expand Down Expand Up @@ -296,27 +296,19 @@ export class ApplicationService {

this.applicationUrl$ = this.appSummaryEntityService.entityObs$.pipe(
map(({ entity }) => entity),
filter(app => !!app),
map(app => {
const routes = app && app.entity.routes ? app.entity.routes : [];
const nonTCPRoutes = routes.filter(p => p && !isTCPRoute(p));
if (nonTCPRoutes.length > 0) {
return nonTCPRoutes[0];
}
return null;
const routes = app.entity.routes ? app.entity.routes : [];
const nonTCPRoutes = routes.filter(p => p && !isTCPRoute(p.entity.port));
return nonTCPRoutes[0] || null;
}),
map(entRoute => {
if (!!entRoute && !!entRoute.entity && !!entRoute.entity.domain) {
return getRoute(entRoute, true, false, {
entityRequestInfo: undefined,
entity: entRoute.entity.domain
});
}
return null;
})
map(entRoute => !!entRoute && !!entRoute.entity && !!entRoute.entity.domain ?
getRoute(entRoute.entity.port, entRoute.entity.host, entRoute.entity.path, true, false, entRoute.entity.domain.name) :
null
)
);
}


isEntityComplete(value, requestInfo: { fetching: boolean }): boolean {
if (requestInfo) {
return !requestInfo.fetching;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
import { CfUserService } from './../../../../shared/data-services/cf-user.service';
import { Component, Inject, NgZone, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Store } from '@ngrx/store';
import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs';
import { delay, filter, first, map, mergeMap, tap, withLatestFrom, startWith, switchMap } from 'rxjs/operators';
import { delay, filter, first, map, mergeMap, startWith, switchMap, tap, withLatestFrom } from 'rxjs/operators';

import { IApp, IOrganization, ISpace } from '../../../../core/cf-api.types';
import { CurrentUserPermissions } from '../../../../core/current-user-permissions.config';
import { CurrentUserPermissionsService } from '../../../../core/current-user-permissions.service';
import { EntityService } from '../../../../core/entity-service';
import {
getActionsFromExtensions,
getTabsFromExtensions,
StratosActionMetadata,
StratosActionType,
StratosTabType,
} from '../../../../core/extension/extension-service';
import { safeUnsubscribe } from '../../../../core/utils.service';
import { ApplicationStateData } from '../../../../shared/components/application-state/application-state.service';
import { ConfirmationDialogConfig } from '../../../../shared/components/confirmation-dialog.config';
import { ConfirmationDialogService } from '../../../../shared/components/confirmation-dialog.service';
import { IHeaderBreadcrumb } from '../../../../shared/components/page-header/page-header.types';
import { ISubHeaderTabs } from '../../../../shared/components/page-subheader/page-subheader.types';
import { ENTITY_SERVICE } from '../../../../shared/entity.tokens';
import { AppMetadataTypes, GetAppStatsAction, GetAppSummaryAction } from '../../../../store/actions/app-metadata.actions';
import { RestageApplication } from '../../../../store/actions/application.actions';
import { ResetPagination } from '../../../../store/actions/pagination.actions';
import { RouterNav } from '../../../../store/actions/router.actions';
import { AppState } from '../../../../store/app-state';
import { applicationSchemaKey, appStatsSchemaKey, entityFactory } from '../../../../store/helpers/entity-factory';
import { ActionState } from '../../../../store/reducers/api-request-reducer/types';
import { endpointEntitiesSelector } from '../../../../store/selectors/endpoint.selectors';
import { APIResource } from '../../../../store/types/api.types';
import { EndpointModel } from '../../../../store/types/endpoint.types';
import { ApplicationService, ApplicationData } from '../../application.service';
import { ApplicationData, ApplicationService } from '../../application.service';
import { EndpointsService } from './../../../../core/endpoints.service';
import { RestageApplication } from '../../../../store/actions/application.actions';
import { ApplicationStateData } from '../../../../shared/components/application-state/application-state.service';
import { ActionState } from '../../../../store/reducers/api-request-reducer/types';
import {
getTabsFromExtensions,
StratosTabType,
StratosActionMetadata,
getActionsFromExtensions,
StratosActionType
} from '../../../../core/extension/extension-service';
import { CurrentUserPermissions } from '../../../../core/current-user-permissions.config';
import { CurrentUserPermissionsService } from '../../../../core/current-user-permissions.service';
import { GitSCMService, GitSCMType } from './../../../../shared/data-services/scm/scm.service';


Expand Down Expand Up @@ -388,7 +389,6 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy {
}

ngOnDestroy() {
this.appSub$.unsubscribe();
this.entityServiceAppRefresh$.unsubscribe();
safeUnsubscribe(this.appSub$, this.entityServiceAppRefresh$);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { GitSCMType } from './../../../../../../shared/data-services/scm/scm.service';
import { GitHubSCM } from './../../../../../../shared/data-services/scm/github-scm';
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { combineLatest, distinct, map, tap, startWith } from 'rxjs/operators';
import { combineLatest, distinct, map, startWith } from 'rxjs/operators';

import { EntityInfo } from '../../../../../../store/types/api.types';
import { AppSummary } from '../../../../../../store/types/app-metadata.types';
import { IAppSummary } from '../../../../../../core/cf-api.types';
import { GitSCMService } from '../../../../../../shared/data-services/scm/scm.service';
import { APIResource, EntityInfo } from '../../../../../../store/types/api.types';
import { getFullEndpointApiUrl } from '../../../../../endpoints/endpoint-helpers';
import { ApplicationMonitorService } from '../../../../application-monitor.service';
import { ApplicationData, ApplicationService } from '../../../../application.service';
import { GitSCMService } from '../../../../../../shared/data-services/scm/scm.service';
import { GitSCMType } from './../../../../../../shared/data-services/scm/scm.service';

const isDockerHubRegEx = /^([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_-]+):([a-zA-Z0-9_.-]+)/g;

Expand Down Expand Up @@ -41,7 +40,7 @@ export class BuildTabComponent implements OnInit {
combineLatest(
this.applicationService.appSummary$
),
map(([app, appSummary]: [ApplicationData, EntityInfo<AppSummary>]) => {
map(([app, appSummary]: [ApplicationData, EntityInfo<APIResource<IAppSummary>>]) => {
return app.fetching || appSummary.entityRequestInfo.fetching;
}), distinct());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { RoutesTabComponent } from './routes-tab.component';
import { BaseTestModules } from '../../../../../../../test-framework/cloud-foundry-endpoint-service.helper';
import { generateTestApplicationServiceProvider } from '../../../../../../../test-framework/application-service-helper';
import { ApplicationEnvVarsHelper } from '../../build-tab/application-env-vars.service';
import { DatePipe } from '@angular/common';

describe('RoutesTabComponent', () => {
let component: RoutesTabComponent;
Expand All @@ -15,7 +16,8 @@ describe('RoutesTabComponent', () => {
imports: [...BaseTestModules],
providers: [
generateTestApplicationServiceProvider('test', 'test'),
ApplicationEnvVarsHelper
ApplicationEnvVarsHelper,
DatePipe
]
})
.compileComponents();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { DatePipe } from '@angular/common';
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { Subscription } from 'rxjs';
import { first } from 'rxjs/operators';

import { CurrentUserPermissionsService } from '../../../../../../../core/current-user-permissions.service';
import { ConfirmationDialogService } from '../../../../../../../shared/components/confirmation-dialog.service';
import {
CfAppRoutesListConfigService,
Expand All @@ -13,8 +15,9 @@ import { FetchAllDomains } from '../../../../../../../store/actions/domains.acti
import { AppState } from '../../../../../../../store/app-state';
import { domainSchemaKey, entityFactory } from '../../../../../../../store/helpers/entity-factory';
import { getPaginationObservables } from '../../../../../../../store/reducers/pagination-reducer/pagination-reducer.helper';
import { APIResource, EntityInfo } from '../../../../../../../store/types/api.types';
import { APIResource } from '../../../../../../../store/types/api.types';
import { ApplicationService } from '../../../../../application.service';
import { CfOrgSpaceDataService } from '../../../../../../../shared/data-services/cf-org-space-service.service';

@Component({
selector: 'app-routes-tab',
Expand All @@ -26,11 +29,15 @@ import { ApplicationService } from '../../../../../application.service';
useFactory: (
store: Store<AppState>,
appService: ApplicationService,
confirmDialog: ConfirmationDialogService) => {
return new CfAppRoutesListConfigService(store, appService, confirmDialog);
confirmDialog: ConfirmationDialogService,
datePipe: DatePipe,
cups: CurrentUserPermissionsService
) => {
return new CfAppRoutesListConfigService(store, appService, confirmDialog, datePipe, cups);
},
deps: [Store, ApplicationService, ConfirmationDialogService]
}
deps: [Store, ApplicationService, ConfirmationDialogService, DatePipe, CurrentUserPermissionsService]
},
CfOrgSpaceDataService
]
})
export class RoutesTabComponent implements OnInit {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,7 @@ export class AddRoutesComponent implements OnInit, OnDestroy {
switchMap(sharedDomains => this.appService.waitForAppEntity$
.pipe(
switchMap(app => {
const space = app.entity.entity.space as APIResource<ISpace>;
this.spaceGuid = space.metadata.guid;
this.spaceGuid = app.entity.entity.space_guid;
const spaceService = this.entityServiceFactory.create<APIResource<ISpace>>(spaceSchemaKey,
entityFactory(spaceSchemaKey),
this.spaceGuid,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DatePipe } from '@angular/common';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
Expand All @@ -20,7 +21,8 @@ describe('MapRoutesComponent', () => {
declarations: [MapRoutesComponent],
providers: [
ListConfig,
{ provide: ApplicationService, useClass: ApplicationServiceMock }
{ provide: ApplicationService, useClass: ApplicationServiceMock },
DatePipe
],
imports: [
CoreModule,
Expand Down
Loading

0 comments on commit f134307

Please sign in to comment.