From 9507d39f2c8027ed32bca44c41cbe4e46cf851b2 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 9 Feb 2018 13:14:11 +0000 Subject: [PATCH 01/30] Fix unregister endpoint - Ensure unregister request is treated as a delete --- .../endpoint/endpoints-list-config.service.ts | 24 +++++++++++++------ .../app/store/effects/endpoint.effects.ts | 20 +++++++++++----- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts index 65096584f6..b071159ad6 100644 --- a/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts @@ -11,7 +11,7 @@ import { ShowSnackBar } from '../../../../../store/actions/snackBar.actions'; import { GetSystemInfo } from '../../../../../store/actions/system.actions'; import { AppState } from '../../../../../store/app-state'; import { EndpointsEffect } from '../../../../../store/effects/endpoint.effects'; -import { selectUpdateInfo } from '../../../../../store/selectors/api.selectors'; +import { selectDeletionInfo, selectUpdateInfo } from '../../../../../store/selectors/api.selectors'; import { EndpointModel, endpointStoreNames } from '../../../../../store/types/endpoint.types'; import { ITableColumn } from '../../list-table/table.types'; import { IListAction, IListConfig, IMultiListAction, ListViewTypes } from '../../list.component.types'; @@ -29,9 +29,8 @@ export class EndpointsListConfigService implements IListConfig { private listActionDelete: IListAction = { action: (item) => { this.store.dispatch(new UnregisterEndpoint(item.guid)); - this.handleAction(item, EndpointsEffect.unregisteringKey, ([oldVal, newVal]) => { + this.handleDeleteAction(item, ([oldVal, newVal]) => { this.store.dispatch(new ShowSnackBar(`Unregistered ${item.name}`)); - this.store.dispatch(new ResetPagination(this.dataSource.entityKey, this.dataSource.paginationKey)); }); }, icon: 'delete', @@ -55,7 +54,7 @@ export class EndpointsListConfigService implements IListConfig { private listActionDisconnect: IListAction = { action: (item) => { this.store.dispatch(new DisconnectEndpoint(item.guid)); - this.handleAction(item, EndpointsEffect.disconnectingKey, ([oldVal, newVal]) => { + this.handleUpdateAction(item, EndpointsEffect.disconnectingKey, ([oldVal, newVal]) => { this.store.dispatch(new ShowSnackBar(`Disconnected ${item.name}`)); this.store.dispatch(new GetSystemInfo()); }); @@ -151,12 +150,23 @@ export class EndpointsListConfigService implements IListConfig { enableTextFilter = true; tableFixedRowHeight = true; - private handleAction(item, effectKey, handleChange) { - const disSub = this.store.select(selectUpdateInfo( + private handleUpdateAction(item, effectKey, handleChange) { + this.handleAction(selectUpdateInfo( endpointStoreNames.type, item.guid, effectKey, - )) + ), handleChange); + } + + private handleDeleteAction(item, handleChange) { + this.handleAction(selectDeletionInfo( + endpointStoreNames.type, + item.guid, + ), handleChange); + } + + private handleAction(storeSelect, handleChange) { + const disSub = this.store.select(storeSelect) .pairwise() .subscribe(([oldVal, newVal]) => { // https://github.com/SUSE/stratos/issues/29 Generic way to handle errors ('Failed to disconnect X') diff --git a/src/frontend/app/store/effects/endpoint.effects.ts b/src/frontend/app/store/effects/endpoint.effects.ts index 174d565146..554d229828 100644 --- a/src/frontend/app/store/effects/endpoint.effects.ts +++ b/src/frontend/app/store/effects/endpoint.effects.ts @@ -47,7 +47,6 @@ export class EndpointsEffect { static connectingKey = 'connecting'; static disconnectingKey = 'disconnecting'; static registeringKey = 'registering'; - static unregisteringKey = 'unregistering'; constructor( private http: HttpClient, @@ -91,7 +90,7 @@ export class EndpointsEffect { @Effect() connectEndpoint$ = this.actions$.ofType(CONNECT_ENDPOINTS) .flatMap(action => { const actionType = 'update'; - const apiAction = this.getEndpointAction(action.guid, action.type, EndpointsEffect.connectingKey); + const apiAction = this.getEndpointUpdateAction(action.guid, action.type, EndpointsEffect.connectingKey); const params: HttpParams = new HttpParams({ fromObject: { 'cnsi_guid': action.guid, @@ -112,7 +111,7 @@ export class EndpointsEffect { @Effect() disconnect$ = this.actions$.ofType(DISCONNECT_ENDPOINTS) .flatMap(action => { - const apiAction = this.getEndpointAction(action.guid, action.type, EndpointsEffect.disconnectingKey); + const apiAction = this.getEndpointUpdateAction(action.guid, action.type, EndpointsEffect.disconnectingKey); const params: HttpParams = new HttpParams({ fromObject: { 'cnsi_guid': action.guid @@ -131,7 +130,7 @@ export class EndpointsEffect { @Effect() unregister$ = this.actions$.ofType(UNREGISTER_ENDPOINTS) .flatMap(action => { - const apiAction = this.getEndpointAction(action.guid, action.type, EndpointsEffect.unregisteringKey); + const apiAction = this.getEndpointDeleteAction(action.guid, action.type); const params: HttpParams = new HttpParams({ fromObject: { 'cnsi_guid': action.guid @@ -150,7 +149,7 @@ export class EndpointsEffect { @Effect() register$ = this.actions$.ofType(REGISTER_ENDPOINTS) .flatMap(action => { - const apiAction = this.getEndpointAction(action.guid(), action.type, EndpointsEffect.registeringKey); + const apiAction = this.getEndpointUpdateAction(action.guid(), action.type, EndpointsEffect.registeringKey); const params: HttpParams = new HttpParams({ fromObject: { 'cnsi_name': action.name, @@ -168,7 +167,8 @@ export class EndpointsEffect { ); }); - private getEndpointAction(guid, type, updatingKey) { + + private getEndpointUpdateAction(guid, type, updatingKey) { return { entityKey: endpointStoreNames.type, guid, @@ -177,6 +177,14 @@ export class EndpointsEffect { } as IRequestAction; } + private getEndpointDeleteAction(guid, type) { + return { + entityKey: endpointStoreNames.type, + guid, + type, + } as IRequestAction; + } + private doEndpointAction( apiAction: IRequestAction, url: string, From b042d69c2a13f913e04ee57f8751f55bfeeca767 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 9 Feb 2018 13:42:39 +0000 Subject: [PATCH 02/30] Non-magical fix - In previous commit the entity was being deleted.. which meant it wasn't shown in table - However it still existed in pagination. Ensure that when we delete we remove it from pagination collections --- src/frontend/app/core/entity-service.ts | 2 +- src/frontend/app/store/effects/endpoint.effects.ts | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/frontend/app/core/entity-service.ts b/src/frontend/app/core/entity-service.ts index 56a108d270..82d6cbb062 100644 --- a/src/frontend/app/core/entity-service.ts +++ b/src/frontend/app/core/entity-service.ts @@ -3,7 +3,7 @@ import { Store } from '@ngrx/store'; import { denormalize, Schema } from 'normalizr'; import { tag } from 'rxjs-spy/operators/tag'; import { interval } from 'rxjs/observable/interval'; -import { filter, map, publishReplay, refCount, shareReplay, tap, withLatestFrom, share } from 'rxjs/operators'; +import { filter, map, shareReplay, tap, withLatestFrom, share } from 'rxjs/operators'; import { Observable } from 'rxjs/Rx'; import { AppState } from '../store/app-state'; diff --git a/src/frontend/app/store/effects/endpoint.effects.ts b/src/frontend/app/store/effects/endpoint.effects.ts index 554d229828..00d9b30f27 100644 --- a/src/frontend/app/store/effects/endpoint.effects.ts +++ b/src/frontend/app/store/effects/endpoint.effects.ts @@ -40,6 +40,7 @@ import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { SystemInfo } from '../types/system.types'; import { map, mergeMap, catchError } from 'rxjs/operators'; import { GetSystemInfo, GET_SYSTEM_INFO, GET_SYSTEM_INFO_SUCCESS, GetSystemSuccess } from '../actions/system.actions'; +import { ClearPaginationOfType, ClearPaginationOfEntity } from '../actions/pagination.actions'; @Injectable() export class EndpointsEffect { @@ -202,6 +203,9 @@ export class EndpointsEffect { if (actionStrings[0]) { this.store.dispatch({ type: actionStrings[0] }); } + if (apiActionType === 'delete') { + this.store.dispatch(new ClearPaginationOfEntity(apiAction.entityKey, apiAction.guid)); + } return new WrapperRequestActionSuccess(null, apiAction, apiActionType); }) .catch(e => { From d8c424a1ed5b60301ebfbc0f3cd498352e8d03a5 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 9 Feb 2018 14:48:20 +0000 Subject: [PATCH 03/30] Fix two spammy issues - shareReplay was not unsubbing again leading to increased Set Page actions - set page actions should only be used when the local list has been filtered --- .../data-sources-controllers/list-data-source.ts | 16 ++++++++++++---- .../endpoint/endpoints-list-config.service.ts | 1 - 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source.ts b/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source.ts index f96477add8..fd4ff7a8a4 100644 --- a/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source.ts +++ b/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source.ts @@ -7,7 +7,7 @@ import { OperatorFunction } from 'rxjs/interfaces'; import { Observable } from 'rxjs/Observable'; import { combineLatest } from 'rxjs/observable/combineLatest'; import { distinctUntilChanged } from 'rxjs/operators'; -import { map, shareReplay } from 'rxjs/operators'; +import { map, shareReplay, publish, refCount, publishReplay, share, filter } from 'rxjs/operators'; import { Subscription } from 'rxjs/Subscription'; import { SetResultCount } from '../../../../store/actions/pagination.actions'; @@ -242,23 +242,31 @@ export abstract class ListDataSource extends DataSource implements pagination$, page$ ).pipe( + filter(([paginationEntity, entities]) => !getCurrentPageRequestInfo(paginationEntity).busy), map(([paginationEntity, entities]) => { + + const entitiesPreFilter = entities.length; if (dataFunctions && dataFunctions.length) { entities = dataFunctions.reduce((value, fn) => { return fn(value, paginationEntity); }, entities); } + const entitiesPostFilter = entities.length; + const pages = this.splitClientPages(entities, paginationEntity.clientPagination.pageSize); if ( - paginationEntity.totalResults !== entities.length || - paginationEntity.clientPagination.totalResults !== entities.length + entitiesPreFilter !== entitiesPostFilter && + (paginationEntity.totalResults !== entities.length || + paginationEntity.clientPagination.totalResults !== entities.length) ) { this.store.dispatch(new SetResultCount(this.entityKey, this.paginationKey, entities.length)); } + const pageIndex = paginationEntity.clientPagination.currentPage - 1; return pages[pageIndex]; }), - shareReplay(1), + publishReplay(1), + refCount(), tag('local-list') ); } diff --git a/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts index b071159ad6..f24f5ca69c 100644 --- a/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/endpoint/endpoints-list-config.service.ts @@ -6,7 +6,6 @@ import { ConnectEndpointDialogComponent, } from '../../../../../features/endpoints/connect-endpoint-dialog/connect-endpoint-dialog.component'; import { DisconnectEndpoint, UnregisterEndpoint } from '../../../../../store/actions/endpoint.actions'; -import { ResetPagination } from '../../../../../store/actions/pagination.actions'; import { ShowSnackBar } from '../../../../../store/actions/snackBar.actions'; import { GetSystemInfo } from '../../../../../store/actions/system.actions'; import { AppState } from '../../../../../store/app-state'; From c4eaf234a65dd18db4e2ad5012d8ef8853119c44 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 9 Feb 2018 16:25:40 +0000 Subject: [PATCH 04/30] Fix org/space shemas - Selecting cf/org/spaces in filter or add/deploy app should work again - Only have one type of Space/Org Schema --- .../applications/application.service.ts | 15 ++- .../create-application-step3.component.ts | 4 +- .../deploy-application-step3.component.ts | 95 +++++++++---------- .../cf-org-space-service.service.ts | 7 +- .../app/store/actions/application.actions.ts | 4 +- .../app/store/actions/organisation.action.ts | 30 ------ .../app/store/actions/organisation.actions.ts | 52 ++++++++++ .../app/store/actions/organization.actions.ts | 42 -------- .../app/store/actions/space.action.ts | 35 ------- .../app/store/actions/space.actions.ts | 43 ++++++--- 10 files changed, 145 insertions(+), 182 deletions(-) delete mode 100644 src/frontend/app/store/actions/organisation.action.ts create mode 100644 src/frontend/app/store/actions/organisation.actions.ts delete mode 100644 src/frontend/app/store/actions/organization.actions.ts delete mode 100644 src/frontend/app/store/actions/space.action.ts diff --git a/src/frontend/app/features/applications/application.service.ts b/src/frontend/app/features/applications/application.service.ts index 33d50f7ade..1267829c4f 100644 --- a/src/frontend/app/features/applications/application.service.ts +++ b/src/frontend/app/features/applications/application.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; -import { map } from 'rxjs/operators'; +import { map, mergeMap } from 'rxjs/operators'; import { EntityService } from '../../core/entity-service'; import { EntityServiceFactory } from '../../core/entity-service-factory.service'; @@ -18,7 +18,6 @@ import { } from '../../store/actions/app-metadata.actions'; import { GetApplication, UpdateApplication, UpdateExistingApplication } from '../../store/actions/application.actions'; import { ApplicationSchema } from '../../store/actions/application.actions'; -import { SpaceSchema } from '../../store/actions/space.action'; import { AppState } from '../../store/app-state'; import { ActionState } from '../../store/reducers/api-request-reducer/types'; import { selectEntity } from '../../store/selectors/api.selectors'; @@ -46,6 +45,7 @@ import { } from './application/application-tabs-base/tabs/build-tab/application-env-vars.service'; import { getRoute, isTCPRoute } from './routes/routes.helper'; import { PaginationMonitor } from '../../shared/monitors/pagination-monitor'; +import { spaceSchemaKey, organisationSchemaKey } from '../../store/actions/action-types'; export interface ApplicationData { fetching: boolean; @@ -157,9 +157,14 @@ export class ApplicationService { .filter(entityInfo => entityInfo.entity && entityInfo.entity.entity && entityInfo.entity.entity.cfGuid) .map(entityInfo => entityInfo.entity.entity) .do(app => { - this.appSpace$ = this.store.select(selectEntity(SpaceSchema.key, app.space_guid)); - // See https://github.com/SUSE/stratos/issues/158 (Failing to populate entity store with a space's org) - this.appOrg$ = this.store.select(selectEntity(SpaceSchema.key, app.space_guid)).map(space => space.entity.organization); + + this.appSpace$ = this.store.select(selectEntity(spaceSchemaKey, app.space_guid)); + this.appOrg$ = this.appSpace$.pipe( + map(space => space.entity.organization_guid), + mergeMap(orgGuid => { + return this.store.select(selectEntity(organisationSchemaKey, orgGuid)); + }) + ); }) .take(1) .subscribe(); diff --git a/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.ts b/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.ts index 8464c650c1..f73bd5d9ed 100644 --- a/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.ts +++ b/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.ts @@ -18,8 +18,8 @@ import { AppState } from '../../../../store/app-state'; import { selectNewAppState } from '../../../../store/effects/create-app-effects'; import { CreateNewApplicationState } from '../../../../store/types/create-application.types'; import { RouterNav } from '../../../../store/actions/router.actions'; -import { OrganisationSchema } from '../../../../store/actions/organisation.action'; import { RequestInfoState } from '../../../../store/reducers/api-request-reducer/types'; +import { organisationSchemaKey } from '../../../../store/actions/action-types'; @Component({ selector: 'app-create-application-step3', @@ -124,7 +124,7 @@ export class CreateApplicationStep3Component implements OnInit { }) .filter(state => state.cloudFoundryDetails && state.cloudFoundryDetails.org) .mergeMap(state => { - return this.store.select(selectEntity(OrganisationSchema.key, state.cloudFoundryDetails.org)) + return this.store.select(selectEntity(organisationSchemaKey, state.cloudFoundryDetails.org)) .first() .map(org => org.entity.domains); }); diff --git a/src/frontend/app/features/applications/deploy-application/deploy-application-step3/deploy-application-step3.component.ts b/src/frontend/app/features/applications/deploy-application/deploy-application-step3/deploy-application-step3.component.ts index e8653b710d..c0aa14ad44 100644 --- a/src/frontend/app/features/applications/deploy-application/deploy-application-step3/deploy-application-step3.component.ts +++ b/src/frontend/app/features/applications/deploy-application/deploy-application-step3/deploy-application-step3.component.ts @@ -4,9 +4,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '../../../../store/app-state'; import { tap, filter, map, mergeMap, combineLatest, switchMap, share, catchError } from 'rxjs/operators'; import { getEntityById, selectEntity, selectEntities } from '../../../../store/selectors/api.selectors'; -import { OrganizationSchema } from '../../../../store/actions/organization.actions'; import { DeleteDeployAppSection } from '../../../../store/actions/deploy-applications.actions'; -import { SpaceSchema } from '../../../../store/actions/space.actions'; import websocketConnect from 'rxjs-websockets'; import { QueueingSubject } from 'queueing-subject/lib'; import { Subscription } from 'rxjs/Subscription'; @@ -20,6 +18,7 @@ import { RouterNav } from '../../../../store/actions/router.actions'; import { GetAllApplications } from '../../../../store/actions/application.actions'; import { environment } from '../../../../../environments/environment'; import { CfOrgSpaceDataService } from '../../../../shared/data-services/cf-org-space-service.service'; +import { organisationSchemaKey, spaceSchemaKey } from '../../../../store/actions/action-types'; @Component({ selector: 'app-deploy-application-step3', @@ -58,9 +57,9 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { && !!appDetail.applicationSource && !!appDetail.applicationSource.projectName), mergeMap(p => { - const orgSubscription = this.store.select(selectEntity(OrganizationSchema.key, p.cloudFoundryDetails.org)); - const spaceSubscription = this.store.select(selectEntity(SpaceSchema.key, p.cloudFoundryDetails.space)); - return Observable.of(p).combineLatest(orgSubscription, spaceSubscription ); + const orgSubscription = this.store.select(selectEntity(organisationSchemaKey, p.cloudFoundryDetails.org)); + const spaceSubscription = this.store.select(selectEntity(spaceSchemaKey, p.cloudFoundryDetails.space)); + return Observable.of(p).combineLatest(orgSubscription, spaceSubscription); }), tap(p => { const host = window.location.host; @@ -70,10 +69,10 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { `?org=${p[1].entity.name}&space=${p[2].entity.name}` ); - const inputStream = new QueueingSubject(); + const inputStream = new QueueingSubject(); this.messages = websocketConnect(streamUrl, inputStream) - .messages.pipe( - catchError(e => { + .messages.pipe( + catchError(e => { return []; }), share(), @@ -88,21 +87,21 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { this.updateTitle(log); } }), - filter((log ) => log.type === SocketEventTypes.DATA), + filter((log) => log.type === SocketEventTypes.DATA), map((log) => { const timesString = moment(log.timestamp * 1000).format('DD/MM/YYYY hh:mm:ss A'); return ( `${timesString}: ${log.message}` ); }) - ); + ); inputStream.next(this.sendProjectInfo(p[0].applicationSource)); }) ).subscribe(); } - sendProjectInfo = (appSource: DeployApplicationSource) => { + sendProjectInfo = (appSource: DeployApplicationSource) => { if (appSource.type.id === 'git') { if (appSource.type.subType === 'github') { return this.sendGitHubSourceMetadata(appSource); @@ -114,7 +113,7 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { return ''; } - sendGitHubSourceMetadata = (appSource: DeployApplicationSource) => { + sendGitHubSourceMetadata = (appSource: DeployApplicationSource) => { const github = { project: appSource.projectName, branch: appSource.branch.name, @@ -129,7 +128,7 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { return JSON.stringify(msg); } - sendGitUrlSourceMetadata = (appSource: DeployApplicationSource) => { + sendGitUrlSourceMetadata = (appSource: DeployApplicationSource) => { const giturl = { url: appSource.projectName, branch: appSource.branch.name, @@ -155,53 +154,53 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { this.appData.org = this.cfOrgSpaceService.org.select.getValue(); this.appData.space = this.cfOrgSpaceService.space.select.getValue(); break; - case SocketEventTypes.EVENT_PUSH_STARTED : - this.streamTitle = 'Deploying...'; - this.store.dispatch(new GetAllApplications('applicationWall')); - break; - case SocketEventTypes.EVENT_PUSH_COMPLETED : - this.streamTitle = 'Deployed'; - this.apps$ = this.store.select(selectEntities('application')).pipe( - tap(apps => { - Object.values(apps).forEach(app => { - if ( - app.entity.space_guid === this.appData.space && - app.entity.cfGuid === this.appData.cloudFoundry && - app.entity.name === this.appData.Name - ) { - this.appGuid = app.entity.guid; - this.validate = Observable.of(true); - } - }); - }) - ).subscribe(); - break; - case SocketEventTypes.CLOSE_SUCCESS : - this.close(log, null, null, true); - break; + case SocketEventTypes.EVENT_PUSH_STARTED: + this.streamTitle = 'Deploying...'; + this.store.dispatch(new GetAllApplications('applicationWall')); + break; + case SocketEventTypes.EVENT_PUSH_COMPLETED: + this.streamTitle = 'Deployed'; + this.apps$ = this.store.select(selectEntities('application')).pipe( + tap(apps => { + Object.values(apps).forEach(app => { + if ( + app.entity.space_guid === this.appData.space && + app.entity.cfGuid === this.appData.cloudFoundry && + app.entity.name === this.appData.Name + ) { + this.appGuid = app.entity.guid; + this.validate = Observable.of(true); + } + }); + }) + ).subscribe(); + break; + case SocketEventTypes.CLOSE_SUCCESS: + this.close(log, null, null, true); + break; case SocketEventTypes.CLOSE_INVALID_MANIFEST: this.close(log, 'Deploy Failed - Invalid manifest!', - 'Failed to deploy app! Please make sure that a valid manifest.yaml was provided!', true); + 'Failed to deploy app! Please make sure that a valid manifest.yaml was provided!', true); break; case SocketEventTypes.CLOSE_NO_MANIFEST: - this.close(log, 'Deploy Failed - No manifest present!', - 'Failed to deploy app! Please make sure that a valid manifest.yaml is present!', true); + this.close(log, 'Deploy Failed - No manifest present!', + 'Failed to deploy app! Please make sure that a valid manifest.yaml is present!', true); break; case SocketEventTypes.CLOSE_FAILED_CLONE: - this.close(log, 'Deploy Failed - Failed to clone repository!', - 'Failed to deploy app! Please make sure the repository is public!', true); + this.close(log, 'Deploy Failed - Failed to clone repository!', + 'Failed to deploy app! Please make sure the repository is public!', true); break; case SocketEventTypes.CLOSE_FAILED_NO_BRANCH: - this.close(log, 'Deploy Failed - Failed to located branch!', - 'Failed to deploy app! Please make sure that branch exists!', true); + this.close(log, 'Deploy Failed - Failed to located branch!', + 'Failed to deploy app! Please make sure that branch exists!', true); break; case SocketEventTypes.CLOSE_FAILURE: case SocketEventTypes.CLOSE_PUSH_ERROR: case SocketEventTypes.CLOSE_NO_SESSION: case SocketEventTypes.CLOSE_NO_CNSI: case SocketEventTypes.CLOSE_NO_CNSI_USERTOKEN: - this.close(log, 'Deploy Failed!', - 'Failed to deploy app!', true); + this.close(log, 'Deploy Failed!', + 'Failed to deploy app!', true); break; case SocketEventTypes.SOURCE_REQUIRED: case SocketEventTypes.EVENT_CLONED: @@ -209,8 +208,8 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { case SocketEventTypes.MANIFEST: break; default: - // noop - } + // noop + } } close(log, title, error, deleteAppSection) { diff --git a/src/frontend/app/shared/data-services/cf-org-space-service.service.ts b/src/frontend/app/shared/data-services/cf-org-space-service.service.ts index b1c80d6657..c4eceaecca 100644 --- a/src/frontend/app/shared/data-services/cf-org-space-service.service.ts +++ b/src/frontend/app/shared/data-services/cf-org-space-service.service.ts @@ -3,12 +3,13 @@ import { Store } from '@ngrx/store'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { Observable } from 'rxjs/Observable'; -import { GetAllOrganizations, OrganizationSchema } from '../../store/actions/organization.actions'; import { AppState } from '../../store/app-state'; import { getPaginationObservables, getCurrentPageRequestInfo } from '../../store/reducers/pagination-reducer/pagination-reducer.helper'; import { endpointsRegisteredEntitiesSelector } from '../../store/selectors/endpoint.selectors'; import { EndpointModel } from '../../store/types/endpoint.types'; import { PaginationMonitorFactory } from '../monitors/pagination-monitor.factory'; +import { GetAllOrganisations } from '../../store/actions/organisation.actions'; +import { OrganisationWithSpaceSchema } from '../../store/actions/action-types'; export interface CfOrgSpaceItem { list$: Observable; @@ -25,7 +26,7 @@ export class CfOrgSpaceDataService { public org: CfOrgSpaceItem; public space: CfOrgSpaceItem; - public paginationAction = new GetAllOrganizations(CfOrgSpaceDataService.CfOrgSpaceServicePaginationKey); + public paginationAction = new GetAllOrganisations(CfOrgSpaceDataService.CfOrgSpaceServicePaginationKey); // TODO: We should optimise this to only fetch the orgs for the current endpoint // (if we inline depth the get orgs request it could be hefty... or we could use a different action to only fetch required data.. @@ -35,7 +36,7 @@ export class CfOrgSpaceDataService { action: this.paginationAction, paginationMonitor: this.paginationMonitorFactory.create( this.paginationAction.paginationKey, - OrganizationSchema + OrganisationWithSpaceSchema ) }); diff --git a/src/frontend/app/store/actions/application.actions.ts b/src/frontend/app/store/actions/application.actions.ts index 3b2fd53de9..596ddb776e 100644 --- a/src/frontend/app/store/actions/application.actions.ts +++ b/src/frontend/app/store/actions/application.actions.ts @@ -5,7 +5,6 @@ import { Headers, RequestOptions, URLSearchParams } from '@angular/http'; import { schema } from 'normalizr'; import { ApiActionTypes } from './request.actions'; -import { SpaceSchema } from './space.actions'; import { StackSchema } from './stack.action'; import { ActionMergeFunction } from '../types/api.types'; import { PaginatedAction } from '../types/pagination.types'; @@ -14,6 +13,7 @@ import { pick } from '../helpers/reducer.helper'; import { AppMetadataTypes } from './app-metadata.actions'; import { AppStatSchema } from '../types/app-metadata.types'; import { getPaginationKey } from './pagination.actions'; +import { SpaceWithOrganisationSchema } from './action-types'; export const GET_ALL = '[Application] Get all'; export const GET_ALL_SUCCESS = '[Application] Get all success'; @@ -50,7 +50,7 @@ export const DELETE_INSTANCE_FAILED = '[Application Instance] Delete failed'; const ApplicationEntitySchema = { entity: { stack: StackSchema, - space: SpaceSchema + space: SpaceWithOrganisationSchema } }; diff --git a/src/frontend/app/store/actions/organisation.action.ts b/src/frontend/app/store/actions/organisation.action.ts deleted file mode 100644 index b59ddf576a..0000000000 --- a/src/frontend/app/store/actions/organisation.action.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { CFStartAction, IRequestAction, ICFAction } from '../types/request.types'; -import { getAPIResourceGuid } from '../selectors/api.selectors'; -import { schema } from 'normalizr'; -import { ApiActionTypes } from './request.actions'; -import { RequestOptions } from '@angular/http'; - -export const GET = '[Organisation] Get one'; -export const GET_SUCCESS = '[Organisation] Get one success'; -export const GET_FAILED = '[Organisation] Get one failed'; - -export const OrganisationSchema = new schema.Entity('organization', {}, { - idAttribute: getAPIResourceGuid -}); - -export class GetOrganisation extends CFStartAction implements ICFAction { - constructor(public guid: string, public endpointGuid: string) { - super(); - this.options = new RequestOptions(); - this.options.url = `organization/${guid}`; - this.options.method = 'get'; - } - actions = [ - GET, - GET_SUCCESS, - GET_FAILED - ]; - entity = [OrganisationSchema]; - entityKey = OrganisationSchema.key; - options: RequestOptions; -} diff --git a/src/frontend/app/store/actions/organisation.actions.ts b/src/frontend/app/store/actions/organisation.actions.ts new file mode 100644 index 0000000000..b86166eb86 --- /dev/null +++ b/src/frontend/app/store/actions/organisation.actions.ts @@ -0,0 +1,52 @@ +import { RequestOptions } from '@angular/http'; + +import { PaginatedAction } from '../types/pagination.types'; +import { CFStartAction, ICFAction } from '../types/request.types'; +import { OrganisationSchema, organisationSchemaKey, OrganisationWithSpaceSchema } from './action-types'; + +export const GET_ORGANISATION = '[Organisation] Get one'; +export const GET_ORGANISATION_SUCCESS = '[Organisation] Get one success'; +export const GET_ORGANISATION_FAILED = '[Organisation] Get one failed'; + +export const GET_ORGANISATIONS = '[Organization] Get all'; +export const GET_ORGANISATIONS_SUCCESS = '[Organization] Get all success'; +export const GET_ORGANISATIONS_FAILED = '[Organization] Get all failed'; + +export class GetOrganisation extends CFStartAction implements ICFAction { + constructor(public guid: string, public endpointGuid: string) { + super(); + this.options = new RequestOptions(); + this.options.url = `organization/${guid}`; + this.options.method = 'get'; + } + actions = [ + GET_ORGANISATION, + GET_ORGANISATION_SUCCESS, + GET_ORGANISATION_FAILED + ]; + entity = [OrganisationSchema]; + entityKey = organisationSchemaKey; + options: RequestOptions; +} + +export class GetAllOrganisations extends CFStartAction implements PaginatedAction { + constructor(public paginationKey: string) { + super(); + this.options = new RequestOptions(); + this.options.url = 'organizations'; + this.options.method = 'get'; + } + actions = [ + GET_ORGANISATIONS, + GET_ORGANISATIONS_SUCCESS, + GET_ORGANISATIONS_FAILED + ]; + entity = [OrganisationWithSpaceSchema]; + entityKey = organisationSchemaKey; + options: RequestOptions; + initialParams = { + page: 1, + 'results-per-page': 100, + 'inline-relations-depth': 1 + }; +} diff --git a/src/frontend/app/store/actions/organization.actions.ts b/src/frontend/app/store/actions/organization.actions.ts deleted file mode 100644 index d02305b3c1..0000000000 --- a/src/frontend/app/store/actions/organization.actions.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { CFStartAction } from '../types/request.types'; -import { getAPIResourceGuid } from '../selectors/api.selectors'; -import { RequestOptions, URLSearchParams } from '@angular/http'; -import { schema } from 'normalizr'; - -import { ApiActionTypes } from './request.actions'; -import { SpaceSchema } from './space.actions'; -import { PaginatedAction } from '../types/pagination.types'; - -export const GET_ALL = '[Organization] Get all'; -export const GET_ALL_SUCCESS = '[Organization] Get all success'; -export const GET_ALL_FAILED = '[Organization] Get all failed'; - -export const OrganizationSchema = new schema.Entity('organization', { - entity: { - spaces: [SpaceSchema] - } -}, { - idAttribute: getAPIResourceGuid - }); - -export class GetAllOrganizations extends CFStartAction implements PaginatedAction { - constructor(public paginationKey: string) { - super(); - this.options = new RequestOptions(); - this.options.url = 'organizations'; - this.options.method = 'get'; - } - actions = [ - GET_ALL, - GET_ALL_SUCCESS, - GET_ALL_FAILED - ]; - entity = [OrganizationSchema]; - entityKey = OrganizationSchema.key; - options: RequestOptions; - initialParams = { - page: 1, - 'results-per-page': 100, - 'inline-relations-depth': 1 - }; -} diff --git a/src/frontend/app/store/actions/space.action.ts b/src/frontend/app/store/actions/space.action.ts deleted file mode 100644 index 2aaa481675..0000000000 --- a/src/frontend/app/store/actions/space.action.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { CFStartAction, IRequestAction, ICFAction } from '../types/request.types'; -import { getAPIResourceGuid } from '../selectors/api.selectors'; -import { schema } from 'normalizr'; -import { ApiActionTypes } from './request.actions'; -import { RequestOptions } from '@angular/http'; -import { OrganisationSchema } from './organisation.action'; - -export const GET = '[Space] Get one'; -export const GET_SUCCESS = '[Space] Get one success'; -export const GET_FAILED = '[Space] Get one failed'; - -export const SpaceSchema = new schema.Entity('space', { - entity: { - organization: OrganisationSchema - } -}, { - idAttribute: getAPIResourceGuid - }); - -export class GetSpace extends CFStartAction implements ICFAction { - constructor(public guid: string, public endpointGuid: string) { - super(); - this.options = new RequestOptions(); - this.options.url = `space/${guid}`; - this.options.method = 'get'; - } - actions = [ - GET, - GET_SUCCESS, - GET_FAILED - ]; - entity = [SpaceSchema]; - entityKey = SpaceSchema.key; - options: RequestOptions; -} diff --git a/src/frontend/app/store/actions/space.actions.ts b/src/frontend/app/store/actions/space.actions.ts index e108607a98..d6b7aa5d07 100644 --- a/src/frontend/app/store/actions/space.actions.ts +++ b/src/frontend/app/store/actions/space.actions.ts @@ -1,19 +1,32 @@ -import { - CFStartAction, - IRequestAction, - ICFAction -} from '../types/request.types'; -import { getAPIResourceGuid } from '../selectors/api.selectors'; import { RequestOptions, URLSearchParams } from '@angular/http'; -import { schema } from 'normalizr'; -import { ApiActionTypes } from './request.actions'; +import { CFStartAction, ICFAction } from '../types/request.types'; +import { SpaceSchema, spaceSchemaKey, SpaceWithOrganisationSchema } from './action-types'; -export const GET_ALL = '[Space] Get all'; -export const GET_ALL_SUCCESS = '[Space] Get all success'; -export const GET_ALL_FAILED = '[Space] Get all failed'; +export const GET_SPACES = '[Space] Get all'; +export const GET_SPACES_SUCCESS = '[Space] Get all success'; +export const GET_SPACES_FAILED = '[Space] Get all failed'; -export const SpaceSchema = new schema.Entity('space'); +export const GET_SPACE = '[Space] Get one'; +export const GET_SPACE_SUCCESS = '[Space] Get one success'; +export const GET_SPACE_FAILED = '[Space] Get one failed'; + +export class GetSpace extends CFStartAction implements ICFAction { + constructor(public guid: string, public endpointGuid: string) { + super(); + this.options = new RequestOptions(); + this.options.url = `space/${guid}`; + this.options.method = 'get'; + } + actions = [ + GET_SPACE, + GET_SPACE_SUCCESS, + GET_SPACE_FAILED + ]; + entity = [SpaceSchema]; + entityKey = spaceSchemaKey; + options: RequestOptions; +} export class GetAllSpaces extends CFStartAction implements ICFAction { constructor(public paginationKey?: string) { @@ -26,8 +39,8 @@ export class GetAllSpaces extends CFStartAction implements ICFAction { this.options.params.set('results-per-page', '100'); this.options.params.set('inline-relations-depth', '1'); } - actions = [GET_ALL, GET_ALL_SUCCESS, GET_ALL_FAILED]; - entity = [SpaceSchema]; - entityKey = SpaceSchema.key; + actions = [GET_SPACES, GET_SPACES_SUCCESS, GET_SPACES_FAILED]; + entity = [SpaceWithOrganisationSchema]; + entityKey = spaceSchemaKey; options: RequestOptions; } From f8482ba595f50f962dbce0f0ff2d95316bf3271c Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 9 Feb 2018 16:47:22 +0000 Subject: [PATCH 05/30] Add missing file --- .../app/store/actions/action-types.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/frontend/app/store/actions/action-types.ts diff --git a/src/frontend/app/store/actions/action-types.ts b/src/frontend/app/store/actions/action-types.ts new file mode 100644 index 0000000000..8c36f51a38 --- /dev/null +++ b/src/frontend/app/store/actions/action-types.ts @@ -0,0 +1,28 @@ +import { schema } from 'normalizr'; +import { getAPIResourceGuid } from '../selectors/api.selectors'; + +export const organisationSchemaKey = 'organization'; +export const OrganisationSchema = new schema.Entity(organisationSchemaKey, {}, { + idAttribute: getAPIResourceGuid +}); + +export const spaceSchemaKey = 'space'; +export const SpaceSchema = new schema.Entity(spaceSchemaKey, {}, { + idAttribute: getAPIResourceGuid +}); + +export const OrganisationWithSpaceSchema = new schema.Entity(organisationSchemaKey, { + entity: { + spaces: [SpaceSchema] + } +}, { + idAttribute: getAPIResourceGuid + }); + +export const SpaceWithOrganisationSchema = new schema.Entity(spaceSchemaKey, { + entity: { + organization: OrganisationSchema + } +}, { + idAttribute: getAPIResourceGuid + }); From fa47ff58ccfd7f46e31de6eae7d55d9f22cd9ee8 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 9 Feb 2018 17:09:05 +0000 Subject: [PATCH 06/30] Common treatment of all input fields and spacing across steppers --- .../create-application-step1.component.html | 44 +++++++++++-------- .../create-application-step3.component.html | 12 ++--- .../add-routes/add-routes.component.scss | 2 +- .../stepper/steppers/steppers.component.scss | 4 +- 4 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/frontend/app/features/applications/create-application/create-application-step1/create-application-step1.component.html b/src/frontend/app/features/applications/create-application/create-application-step1/create-application-step1.component.html index d2afd5df08..aacf9e74fd 100644 --- a/src/frontend/app/features/applications/create-application/create-application-step1/create-application-step1.component.html +++ b/src/frontend/app/features/applications/create-application/create-application-step1/create-application-step1.component.html @@ -1,22 +1,28 @@ Select a Cloud Foundry instance, organization and space for the app.
- - - {{ cf.name }} - - - - - - - {{ org.name }} - - - - - - - {{ space.name }} - - + + + + {{ cf.name }} + + + + + + + + + {{ org.name }} + + + + + + + + + {{ space.name }} + + +
diff --git a/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.html b/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.html index 5478dbe38d..b0e5369214 100644 --- a/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.html +++ b/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.html @@ -1,10 +1,12 @@ Create a route
- - - {{ domain.entity.name }} - - + + + + {{ domain.entity.name }} + + + diff --git a/src/frontend/app/features/applications/routes/add-routes/add-routes.component.scss b/src/frontend/app/features/applications/routes/add-routes/add-routes.component.scss index f1bf78842c..41846fea35 100644 --- a/src/frontend/app/features/applications/routes/add-routes/add-routes.component.scss +++ b/src/frontend/app/features/applications/routes/add-routes/add-routes.component.scss @@ -32,7 +32,7 @@ margin-bottom: 24px; } &__toggle { - margin-bottom: 24px; + margin-bottom: 10px; margin-top: 24px; mat-checkbox { margin-left: 12px; diff --git a/src/frontend/app/shared/components/stepper/steppers/steppers.component.scss b/src/frontend/app/shared/components/stepper/steppers/steppers.component.scss index 3c7e870957..3c801fe2cd 100644 --- a/src/frontend/app/shared/components/stepper/steppers/steppers.component.scss +++ b/src/frontend/app/shared/components/stepper/steppers/steppers.component.scss @@ -74,10 +74,10 @@ form { display: flex; flex-direction: column; + margin-top: 10px; max-width: 60%; - mat-select, mat-form-field { - margin-top: 20px; + padding-top: 10px; width: 100%; } } From 4b7c316d5dc745014f2d80ebba6188ab649bd7ac Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 9 Feb 2018 17:18:26 +0000 Subject: [PATCH 07/30] Ensure top level stepper form styles only apply to top level stopper forms - In the future these should be wrapped in their own component --- .../create-application-step1.component.html | 2 +- .../create-application-step2.component.html | 2 +- .../create-application-step3.component.html | 2 +- .../deploy-application-step2.component.html | 2 +- .../edit-application/edit-application.component.html | 6 +++--- .../routes/add-routes/add-routes.component.html | 4 ++-- .../create-endpoint-cf-step-1.component.html | 8 +++----- .../uaa-wizard/console-uaa-wizard.component.html | 7 +++---- .../components/stepper/steppers/steppers.component.scss | 3 ++- 9 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/frontend/app/features/applications/create-application/create-application-step1/create-application-step1.component.html b/src/frontend/app/features/applications/create-application/create-application-step1/create-application-step1.component.html index aacf9e74fd..a7ae7fd1d7 100644 --- a/src/frontend/app/features/applications/create-application/create-application-step1/create-application-step1.component.html +++ b/src/frontend/app/features/applications/create-application/create-application-step1/create-application-step1.component.html @@ -1,5 +1,5 @@ Select a Cloud Foundry instance, organization and space for the app. - + diff --git a/src/frontend/app/features/applications/create-application/create-application-step2/create-application-step2.component.html b/src/frontend/app/features/applications/create-application/create-application-step2/create-application-step2.component.html index 989afef1a1..8a85fed3e8 100644 --- a/src/frontend/app/features/applications/create-application/create-application-step2/create-application-step2.component.html +++ b/src/frontend/app/features/applications/create-application/create-application-step2/create-application-step2.component.html @@ -1,5 +1,5 @@ Please select a unique name for your application - +
diff --git a/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.html b/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.html index b0e5369214..2d256dc25a 100644 --- a/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.html +++ b/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.html @@ -1,5 +1,5 @@ Create a route - + diff --git a/src/frontend/app/features/applications/deploy-application/deploy-application-step2/deploy-application-step2.component.html b/src/frontend/app/features/applications/deploy-application/deploy-application-step2/deploy-application-step2.component.html index 6196c12c38..e84e320ac5 100644 --- a/src/frontend/app/features/applications/deploy-application/deploy-application-step2/deploy-application-step2.component.html +++ b/src/frontend/app/features/applications/deploy-application/deploy-application-step2/deploy-application-step2.component.html @@ -1,5 +1,5 @@ Please specify the source - +
diff --git a/src/frontend/app/features/applications/edit-application/edit-application.component.html b/src/frontend/app/features/applications/edit-application/edit-application.component.html index adf7442457..965a1589e8 100644 --- a/src/frontend/app/features/applications/edit-application/edit-application.component.html +++ b/src/frontend/app/features/applications/edit-application/edit-application.component.html @@ -9,14 +9,14 @@

Edit Application: {{ (applicationService.application$ | async)?.app.entity.n
- + Application name is required Application name already taken
-
+
@@ -28,7 +28,7 @@

Edit Application: {{ (applicationService.application$ | async)?.app.entity.n Enable SSH to Application Instances - Production Application + Production Application

There was an error while updating the application.

diff --git a/src/frontend/app/features/applications/routes/add-routes/add-routes.component.html b/src/frontend/app/features/applications/routes/add-routes/add-routes.component.html index 60d5af981c..9ad995b467 100644 --- a/src/frontend/app/features/applications/routes/add-routes/add-routes.component.html +++ b/src/frontend/app/features/applications/routes/add-routes/add-routes.component.html @@ -19,7 +19,7 @@ Create TCP Route
-
+
@@ -41,7 +41,7 @@
-
+
diff --git a/src/frontend/app/features/endpoints/create-endpoint/create-endpoint-cf-step-1/create-endpoint-cf-step-1.component.html b/src/frontend/app/features/endpoints/create-endpoint/create-endpoint-cf-step-1/create-endpoint-cf-step-1.component.html index 6a7f0bb10e..f7d8d14b03 100644 --- a/src/frontend/app/features/endpoints/create-endpoint/create-endpoint-cf-step-1/create-endpoint-cf-step-1.component.html +++ b/src/frontend/app/features/endpoints/create-endpoint/create-endpoint-cf-step-1/create-endpoint-cf-step-1.component.html @@ -2,21 +2,19 @@ Register an existing Cloud Foundry endpoint to allow your development team to create and manage their applications.

- When registering, choose a unique, recognizable name so that your developers can easily know which Cloud Foundry endpoint - they are working with. + When registering, choose a unique, recognizable name so that your developers can easily know which Cloud Foundry endpoint they are working with.

Supply the API Url for your Cloud Foundry endpoint

- + Name is required Name is not unique - + URL is required Invalid API URL URL is not unique diff --git a/src/frontend/app/features/uaa-setup/uaa-wizard/console-uaa-wizard.component.html b/src/frontend/app/features/uaa-setup/uaa-wizard/console-uaa-wizard.component.html index e4b78b3475..2a21614446 100644 --- a/src/frontend/app/features/uaa-setup/uaa-wizard/console-uaa-wizard.component.html +++ b/src/frontend/app/features/uaa-setup/uaa-wizard/console-uaa-wizard.component.html @@ -4,15 +4,14 @@

Welcome to SUSE Cloud Foundry Console.

- SUSE Cloud Foundry Console is an Open Source Web-based UI (Console) for managing Cloud Foundry. It allows users and administrators - to both manage applications running in the Cloud Foundry cluster and perform cluster management tasks. + SUSE Cloud Foundry Console is an Open Source Web-based UI (Console) for managing Cloud Foundry. It allows users and administrators to both manage applications running in the Cloud Foundry cluster and perform cluster management tasks.

Before accessing the console for the first time some configuration information is required. Press NEXT to get started.

- + UAA Endpoint @@ -33,7 +32,7 @@ -
+ {{ scope }} diff --git a/src/frontend/app/shared/components/stepper/steppers/steppers.component.scss b/src/frontend/app/shared/components/stepper/steppers/steppers.component.scss index 3c801fe2cd..3372b5febc 100644 --- a/src/frontend/app/shared/components/stepper/steppers/steppers.component.scss +++ b/src/frontend/app/shared/components/stepper/steppers/steppers.component.scss @@ -71,7 +71,8 @@ flex: 1; text-align: right; } - form { + .stepper-form { + // Use a specific style instead of form element selected. This prevents these generic styles bleading into child components of a stepper display: flex; flex-direction: column; margin-top: 10px; From fa032677e1485699270f14b53191aaa0bee04289 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 12 Feb 2018 11:36:52 +0000 Subject: [PATCH 08/30] Convert routes lists (app summary + map) to use local pagination - Converted to local lists - Applied sorting - Ensure route entity has values built in to support sorting --- .../applications/application.service.ts | 1 - .../list-data-source.ts | 5 ++ .../components/list/list-table/table.types.ts | 4 +- .../cf-app-map-routes-list-config.service.ts | 27 +++++++--- .../app-route/cf-app-routes-data-source.ts | 26 +++++++++- .../cf-app-routes-list-config.service.ts | 51 ++++++++++++------- .../table-cell-app-route.component.ts | 2 +- .../table-cell-tcproute.component.html | 4 +- .../table-cell-tcproute.component.ts | 8 +-- .../app/store/actions/route.actions.ts | 19 ++++--- 10 files changed, 101 insertions(+), 46 deletions(-) diff --git a/src/frontend/app/features/applications/application.service.ts b/src/frontend/app/features/applications/application.service.ts index 1267829c4f..1944002e79 100644 --- a/src/frontend/app/features/applications/application.service.ts +++ b/src/frontend/app/features/applications/application.service.ts @@ -157,7 +157,6 @@ export class ApplicationService { .filter(entityInfo => entityInfo.entity && entityInfo.entity.entity && entityInfo.entity.entity.cfGuid) .map(entityInfo => entityInfo.entity.entity) .do(app => { - this.appSpace$ = this.store.select(selectEntity(spaceSchemaKey, app.space_guid)); this.appOrg$ = this.appSpace$.pipe( map(space => space.entity.organization_guid), diff --git a/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source.ts b/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source.ts index f96477add8..a6d351634e 100644 --- a/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source.ts +++ b/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source.ts @@ -113,8 +113,13 @@ export abstract class ListDataSource extends DataSource implements // Add any additional functions via an optional listConfig, such as sorting from the column definition const listColumns = this.config.listConfig ? this.config.listConfig.getColumns() : []; listColumns.forEach(column => { + if (!column.sort) { + return; + } if (DataFunctionDefinition.is(column.sort)) { transformEntities.push(column.sort as DataFunctionDefinition); + } else if (typeof column.sort !== 'boolean') { + transformEntities.push(column.sort as DataFunction); } }); diff --git a/src/frontend/app/shared/components/list/list-table/table.types.ts b/src/frontend/app/shared/components/list/list-table/table.types.ts index 1e9a99f898..e8a51c8399 100644 --- a/src/frontend/app/shared/components/list/list-table/table.types.ts +++ b/src/frontend/app/shared/components/list/list-table/table.types.ts @@ -1,4 +1,4 @@ -import { DataFunctionDefinition } from '../data-sources-controllers/list-data-source'; +import { DataFunction, DataFunctionDefinition } from '../data-sources-controllers/list-data-source'; import { TableCellStatusDirective } from './table-cell-status.directive'; import { listTableCells, TableCellComponent } from './table-cell/table-cell.component'; import { TableRowComponent } from './table-row/table-row.component'; @@ -12,7 +12,7 @@ export interface ITableColumn { headerCell?: () => string; // Either headerCell OR headerCellComponent should be defined headerCellComponent?: any; class?: string; - sort?: boolean | DataFunctionDefinition; + sort?: boolean | DataFunctionDefinition | DataFunction; cellFlex?: string; } diff --git a/src/frontend/app/shared/components/list/list-types/app-route/cf-app-map-routes-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/app-route/cf-app-map-routes-list-config.service.ts index 2954d71c91..34eeb0c719 100644 --- a/src/frontend/app/shared/components/list/list-types/app-route/cf-app-map-routes-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/app-route/cf-app-map-routes-list-config.service.ts @@ -1,3 +1,4 @@ +import { isTCPRoute } from '../../../../../features/applications/routes/routes.helper'; import { Injectable } from '@angular/core'; import { MatSnackBar } from '@angular/material'; import { ActivatedRoute } from '@angular/router'; @@ -25,10 +26,10 @@ import { TableCellAppRouteComponent } from './table-cell-app-route/table-cell-ap import { TableCellRadioComponent } from './table-cell-radio/table-cell-radio.component'; import { TableCellRouteComponent } from './table-cell-route/table-cell-route.component'; import { TableCellTCPRouteComponent } from './table-cell-tcproute/table-cell-tcproute.component'; +import { PaginationEntityState } from '../../../../../store/types/pagination.types'; @Injectable() -export class CfAppMapRoutesListConfigService - implements IListConfig { +export class CfAppMapRoutesListConfigService implements IListConfig { routesDataSource: CfAppRoutesDataSource; columns: Array> = [ @@ -43,21 +44,33 @@ export class CfAppMapRoutesListConfigService columnId: 'route', headerCell: () => 'Route', cellComponent: TableCellRouteComponent, - sort: true, + sort: { + type: 'sort', + orderKey: 'route', + field: 'entity.host' + }, cellFlex: '3' }, { columnId: 'tcproute', headerCell: () => 'TCP Route', cellComponent: TableCellTCPRouteComponent, - sort: true, + sort: { + type: 'sort', + orderKey: 'tcproute', + field: 'entity.isTCPRoute' + }, cellFlex: '3' }, { columnId: 'attachedApps', headerCell: () => 'Apps Attached', cellComponent: TableCellAppRouteComponent, - sort: true, + sort: { + type: 'sort', + orderKey: 'attachedApps', + field: 'entity.mappedAppsCount' + }, cellFlex: '3' } ]; @@ -67,6 +80,7 @@ export class CfAppMapRoutesListConfigService text: { title: 'Available Routes'; }; + isLocal: true; dispatchDeleteAction(route) { return this.store.dispatch( @@ -104,7 +118,8 @@ export class CfAppMapRoutesListConfigService this.appService, new GetSpaceRoutes(spaceGuid, appService.cfGuid), getPaginationKey('route', appService.cfGuid, spaceGuid), - true + true, + this ); } } diff --git a/src/frontend/app/shared/components/list/list-types/app-route/cf-app-routes-data-source.ts b/src/frontend/app/shared/components/list/list-types/app-route/cf-app-routes-data-source.ts index 113909c079..2f775ebcfd 100644 --- a/src/frontend/app/shared/components/list/list-types/app-route/cf-app-routes-data-source.ts +++ b/src/frontend/app/shared/components/list/list-types/app-route/cf-app-routes-data-source.ts @@ -8,6 +8,8 @@ import { APIResource, EntityInfo } from '../../../../../store/types/api.types'; import { PaginatedAction } from '../../../../../store/types/pagination.types'; import { ListDataSource } from '../../data-sources-controllers/list-data-source'; import { IListConfig } from '../../list.component.types'; +import { map } from 'rxjs/operators'; +import { isTCPRoute, getMappedApps } from '../../../../../features/applications/routes/routes.helper'; export const RouteSchema = new schema.Entity('route'); @@ -20,7 +22,8 @@ export class CfAppRoutesDataSource extends ListDataSource { appService: ApplicationService, action: PaginatedAction, paginationKey: string, - mapRoute = false + mapRoute = false, + listConfig: IListConfig ) { super({ store, @@ -28,7 +31,26 @@ export class CfAppRoutesDataSource extends ListDataSource { schema: RouteSchema, getRowUniqueId: (object: EntityInfo) => object.entity ? object.entity.guid : null, - paginationKey + paginationKey, + isLocal: true, + listConfig, + transformEntity: map((routes) => { + routes = routes.map(route => { + let newRoute = route; + if (!route.entity.isTCPRoute || !route.entity.mappedAppsCount) { + newRoute = { + ...route, + entity: { + ...route.entity, + isTCPRoute: isTCPRoute(route), + mappedAppsCount: getMappedApps(route).length + } + }; + } + return newRoute; + }); + return routes; + }) }); this.cfGuid = appService.cfGuid; diff --git a/src/frontend/app/shared/components/list/list-types/app-route/cf-app-routes-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/app-route/cf-app-routes-list-config.service.ts index 521291e6f8..7aa8b52b26 100644 --- a/src/frontend/app/shared/components/list/list-types/app-route/cf-app-routes-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/app-route/cf-app-routes-list-config.service.ts @@ -87,22 +87,22 @@ export class CfAppRoutesListConfigService implements IListConfig { action: () => { this.appService.application$ .pipe( - take(1), - tap(app => { - this.store.dispatch( - new RouterNav({ - path: [ - 'applications', - this.appService.cfGuid, - this.appService.appGuid, - 'add-route' - ], - query: { - spaceGuid: app.app.entity.space_guid - } - }) - ); - }) + take(1), + tap(app => { + this.store.dispatch( + new RouterNav({ + path: [ + 'applications', + this.appService.cfGuid, + this.appService.appGuid, + 'add-route' + ], + query: { + spaceGuid: app.app.entity.space_guid + } + }) + ); + }) ) .subscribe(); }, @@ -118,13 +118,23 @@ export class CfAppRoutesListConfigService implements IListConfig { columnId: 'route', headerCell: () => 'Route', cellComponent: TableCellRouteComponent, - cellFlex: '4' + cellFlex: '4', + sort: { + type: 'sort', + orderKey: 'route', + field: 'entity.host' + } }, { columnId: 'tcproute', headerCell: () => 'TCP Route', cellComponent: TableCellTCPRouteComponent, - cellFlex: '4' + cellFlex: '4', + sort: { + type: 'sort', + orderKey: 'tcproute', + field: 'entity.isTCPRoute' + }, } ]; @@ -133,6 +143,7 @@ export class CfAppRoutesListConfigService implements IListConfig { text = { title: 'Routes' }; + isLocal = true; dispatchDeleteAction(route) { return this.store.dispatch( @@ -169,7 +180,9 @@ export class CfAppRoutesListConfigService implements IListConfig { this.store, this.appService, new GetAppRoutes(appService.appGuid, appService.cfGuid), - getPaginationKey('route', appService.cfGuid, appService.appGuid) + getPaginationKey('route', appService.cfGuid, appService.appGuid), + false, + this ); } diff --git a/src/frontend/app/shared/components/list/list-types/app-route/table-cell-app-route/table-cell-app-route.component.ts b/src/frontend/app/shared/components/list/list-types/app-route/table-cell-app-route/table-cell-app-route.component.ts index 3dee651648..256da4b317 100644 --- a/src/frontend/app/shared/components/list/list-types/app-route/table-cell-app-route/table-cell-app-route.component.ts +++ b/src/frontend/app/shared/components/list/list-types/app-route/table-cell-app-route/table-cell-app-route.component.ts @@ -20,7 +20,7 @@ export class TableCellAppRouteComponent extends TableCellCustom ngOnInit(): void { const apps = this.row.entity.apps; - this.mappedAppsCount = getMappedApps(this.row).length; + this.mappedAppsCount = this.row.entity.mappedAppsCount; const foundApp = apps && apps.find(a => a.metadata.guid === this.appService.appGuid); if (foundApp && foundApp.length !== 0) { diff --git a/src/frontend/app/shared/components/list/list-types/app-route/table-cell-tcproute/table-cell-tcproute.component.html b/src/frontend/app/shared/components/list/list-types/app-route/table-cell-tcproute/table-cell-tcproute.component.html index eb48b9781c..99a0cebf5a 100644 --- a/src/frontend/app/shared/components/list/list-types/app-route/table-cell-tcproute/table-cell-tcproute.component.html +++ b/src/frontend/app/shared/components/list/list-types/app-route/table-cell-tcproute/table-cell-tcproute.component.html @@ -1,8 +1,8 @@
-
+
Yes
-
+
No
diff --git a/src/frontend/app/shared/components/list/list-types/app-route/table-cell-tcproute/table-cell-tcproute.component.ts b/src/frontend/app/shared/components/list/list-types/app-route/table-cell-tcproute/table-cell-tcproute.component.ts index 4c68f3592a..217880e089 100644 --- a/src/frontend/app/shared/components/list/list-types/app-route/table-cell-tcproute/table-cell-tcproute.component.ts +++ b/src/frontend/app/shared/components/list/list-types/app-route/table-cell-tcproute/table-cell-tcproute.component.ts @@ -7,16 +7,10 @@ import { isTCPRoute } from '../../../../../../features/applications/routes/route templateUrl: './table-cell-tcproute.component.html', styleUrls: ['./table-cell-tcproute.component.scss'] }) -export class TableCellTCPRouteComponent extends TableCellCustom implements OnInit { +export class TableCellTCPRouteComponent extends TableCellCustom { @Input('row') row; - isRouteTCP: boolean; constructor() { super(); } - - ngOnInit() { - this.isRouteTCP = isTCPRoute(this.row); - } - } diff --git a/src/frontend/app/store/actions/route.actions.ts b/src/frontend/app/store/actions/route.actions.ts index a6d9c8980b..3663c86de0 100644 --- a/src/frontend/app/store/actions/route.actions.ts +++ b/src/frontend/app/store/actions/route.actions.ts @@ -131,7 +131,6 @@ export class CheckRouteExists extends CFStartAction implements ICFAction { endpointGuid: string; } -// Refactor to satisfy CodeClimate export class ListRoutes extends CFStartAction implements PaginatedAction { constructor( public guid: string, @@ -151,10 +150,8 @@ export class ListRoutes extends CFStartAction implements PaginatedAction { entity = [RouteSchema]; entityKey = RouteSchema.key; options: RequestOptions; - initialParams = { - 'inline-relations-depth': '2' - }; endpointGuid: string; + flattenPagination = true; } export class GetAppRoutes extends ListRoutes implements PaginatedAction { @@ -166,8 +163,11 @@ export class GetAppRoutes extends ListRoutes implements PaginatedAction { ]); } initialParams = { - 'results-per-page': 9, // Match that of the page size used by the matching list config - 'inline-relations-depth': '1' + 'results-per-page': 100, + 'inline-relations-depth': '1', + page: 1, + 'order-direction': 'desc', + 'order-direction-field': 'route', }; } @@ -179,6 +179,13 @@ export class GetSpaceRoutes extends ListRoutes implements PaginatedAction { RouteEvents.GET_SPACE_ALL_FAILED ]); } + initialParams = { + 'results-per-page': 100, + 'inline-relations-depth': '1', + page: 1, + 'order-direction': 'desc', + 'order-direction-field': 'attachedApps', + }; } export class MapRouteSelected implements Action { From e5cba863c7e50eba62191effd0a50c6235551090 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 12 Feb 2018 16:16:37 +0000 Subject: [PATCH 09/30] Show smaller page sizes + ensure any default page size is valid --- .../cf-app-map-routes-list-config.service.ts | 8 ++++---- .../app-route/cf-app-routes-list-config.service.ts | 2 +- .../app/shared/components/list/list.component.ts | 13 +++++++++++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/frontend/app/shared/components/list/list-types/app-route/cf-app-map-routes-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/app-route/cf-app-map-routes-list-config.service.ts index 34eeb0c719..2075fb2fa0 100644 --- a/src/frontend/app/shared/components/list/list-types/app-route/cf-app-map-routes-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/app-route/cf-app-map-routes-list-config.service.ts @@ -75,12 +75,12 @@ export class CfAppMapRoutesListConfigService implements IListConfig } ]; - pageSizeOptions = [9, 45, 90]; + pageSizeOptions = [5, 15, 30]; viewType = ListViewTypes.TABLE_ONLY; - text: { - title: 'Available Routes'; + text = { + title: 'Available Routes' }; - isLocal: true; + isLocal = true; dispatchDeleteAction(route) { return this.store.dispatch( diff --git a/src/frontend/app/shared/components/list/list-types/app-route/cf-app-routes-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/app-route/cf-app-routes-list-config.service.ts index 7aa8b52b26..82b5742ff0 100644 --- a/src/frontend/app/shared/components/list/list-types/app-route/cf-app-routes-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/app-route/cf-app-routes-list-config.service.ts @@ -138,7 +138,7 @@ export class CfAppRoutesListConfigService implements IListConfig { } ]; - pageSizeOptions = [9, 45, 90]; + pageSizeOptions = [5, 15, 30]; viewType = ListViewTypes.TABLE_ONLY; text = { title: 'Routes' diff --git a/src/frontend/app/shared/components/list/list.component.ts b/src/frontend/app/shared/components/list/list.component.ts index d2a2bc5856..c8fe2db7c6 100644 --- a/src/frontend/app/shared/components/list/list.component.ts +++ b/src/frontend/app/shared/components/list/list.component.ts @@ -82,11 +82,11 @@ export class ListComponent implements OnInit, OnDestroy, AfterViewInit { this.dataSource = this.config.getDataSource(); this.multiFilterConfigs = this.config.getMultiFiltersConfigs(); - // Set up an obervable containing the current view (card/table) + // Set up an observable containing the current view (card/table) const { view, } = getListStateObservables(this.store, this.dataSource.paginationKey); this.view$ = view; - // If this is the first time the user has used this lis then set the view to the default + // If this is the first time the user has used this list then set the view to the default this.view$.first().subscribe(listView => { if (!listView) { this.updateListView(this.config.defaultView || 'table'); @@ -96,6 +96,15 @@ export class ListComponent implements OnInit, OnDestroy, AfterViewInit { this.paginationController = new ListPaginationController(this.store, this.dataSource); this.paginator.pageSizeOptions = this.config.pageSizeOptions; + + // Ensure we set a pageSize that's relevant to the configured set of page sizes. The default is 9 and in some cases is not a valid + // pageSize + this.paginationController.pagination$.first().subscribe(pagination => { + if (this.paginator.pageSizeOptions.findIndex(pageSize => pageSize === pagination.pageSize) < 0) { + this.paginationController.pageSize(this.paginator.pageSizeOptions[0]); + } + }); + const paginationStoreToWidget = this.paginationController.pagination$.do((pagination: ListPagination) => { this.paginator.length = pagination.totalResults; this.paginator.pageIndex = pagination.pageIndex - 1; From c693428698bc58f110be129bcd25d6d5f7b58c10 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 12 Feb 2018 16:29:41 +0000 Subject: [PATCH 10/30] Fix delete --- src/frontend/app/store/actions/route.actions.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/frontend/app/store/actions/route.actions.ts b/src/frontend/app/store/actions/route.actions.ts index 3663c86de0..645ad13c79 100644 --- a/src/frontend/app/store/actions/route.actions.ts +++ b/src/frontend/app/store/actions/route.actions.ts @@ -63,14 +63,14 @@ export class CreateRoute extends CFStartAction implements ICFAction { export class DeleteRoute extends CFStartAction implements ICFAction { constructor( - public routeGuid: string, + public guid: string, public cfGuid: string, public async: boolean = false, public recursive: boolean = true ) { super(); this.options = new RequestOptions(); - this.options.url = `routes/${routeGuid}`; + this.options.url = `routes/${guid}`; this.options.method = 'delete'; this.options.params = new URLSearchParams(); this.options.params.append('recursive', recursive ? 'true' : 'false'); @@ -86,6 +86,7 @@ export class DeleteRoute extends CFStartAction implements ICFAction { entityKey = RouteSchema.key; options: RequestOptions; endpointGuid: string; + removeEntityOnDelete = true; } export class UnmapRoute extends CFStartAction implements ICFAction { From 6192c7e7405bd738a0fe6ad3aeceff7a2c0359be Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 13 Feb 2018 16:47:26 +0000 Subject: [PATCH 11/30] Create neater, smaller list header - Moves smaller header flush with table - Smaller header look and feel the same when switching between app wall list and card view - BEMin --- .gitignore | 1 + .../variables-tab.component.html | 2 +- .../variables-tab.component.scss | 9 + .../list/list-cards/cards.component.scss | 1 + .../list/list-table/table.component.html | 2 - .../list/list-table/table.component.scss | 1 - .../components/list/list.component.html | 175 +++++++++--------- .../components/list/list.component.scss | 86 ++++----- .../shared/components/list/list.component.ts | 14 ++ 9 files changed, 150 insertions(+), 141 deletions(-) diff --git a/.gitignore b/.gitignore index 0578047c63..a4084e37b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # compiled output /dist +.dist /tmp /out-tsc /out diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/variables-tab/variables-tab.component.html b/src/frontend/app/features/applications/application/application-tabs-base/tabs/variables-tab/variables-tab.component.html index 0c15bc32b3..f1ac432f0a 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/variables-tab/variables-tab.component.html +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/variables-tab/variables-tab.component.html @@ -1,7 +1,7 @@
-
+
diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/variables-tab/variables-tab.component.scss b/src/frontend/app/features/applications/application/application-tabs-base/tabs/variables-tab/variables-tab.component.scss index a6278dcca7..76ca0d369b 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/variables-tab/variables-tab.component.scss +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/variables-tab/variables-tab.component.scss @@ -11,4 +11,13 @@ } } } + &__env-table--add-form { + form { + display: flex; + flex-direction: row; + } + mat-form-field { + padding-right: 10px; + } + } } diff --git a/src/frontend/app/shared/components/list/list-cards/cards.component.scss b/src/frontend/app/shared/components/list/list-cards/cards.component.scss index 118ab7a93e..1a30ec14da 100644 --- a/src/frontend/app/shared/components/list/list-cards/cards.component.scss +++ b/src/frontend/app/shared/components/list/list-cards/cards.component.scss @@ -2,6 +2,7 @@ display: flex; flex-flow: wrap; justify-content: space-between; + padding-top: 20px; .mat-card { &::after { bottom: 0; diff --git a/src/frontend/app/shared/components/list/list-table/table.component.html b/src/frontend/app/shared/components/list/list-table/table.component.html index e73f029d41..f7eea57052 100644 --- a/src/frontend/app/shared/components/list/list-table/table.component.html +++ b/src/frontend/app/shared/components/list/list-table/table.component.html @@ -1,7 +1,5 @@
- - diff --git a/src/frontend/app/shared/components/list/list-table/table.component.scss b/src/frontend/app/shared/components/list/list-table/table.component.scss index 06a34312de..4b4c038849 100644 --- a/src/frontend/app/shared/components/list/list-table/table.component.scss +++ b/src/frontend/app/shared/components/list/list-table/table.component.scss @@ -2,7 +2,6 @@ $row-height: 62px; .app-table { &__card { padding: 0; - padding-top: 24px; } &__inner { &[hidden] * { diff --git a/src/frontend/app/shared/components/list/list.component.html b/src/frontend/app/shared/components/list/list.component.html index a9a37cb764..7ac9bcf22e 100644 --- a/src/frontend/app/shared/components/list/list.component.html +++ b/src/frontend/app/shared/components/list/list.component.html @@ -1,109 +1,104 @@
+ - -
-
-
{{ config.text?.title }}
-
{{dataSource.selectedRows.size}} Selected
-
-
-
- - - -
-
- - - -
-
-
- - - -
-
- -
-
- - -
-
+
+
{{ config.text?.title }}
+
{{dataSource.selectedRows.size}} Selected
+ +
+ + + All + + {{selectItem.label}} + + + {{multiFilterConfig.label}} +
-
-
-
- - - All - - {{selectItem.label}} - - - -
-
-
-
- - - - - - {{column.headerCell()}} - - - -
-
- - - -
-
+
+
+ +
+ + + +
+ +
+ + + + + + {{column.headerCell()}} + + + +
+ +
+ + + +
+ +
+ + + +
+ +
+
+ + + +
+ +
+ +
+ +
+ +
- - -
-
-
-
+
- - + +
- + - +
There are no entries.
diff --git a/src/frontend/app/shared/components/list/list.component.scss b/src/frontend/app/shared/components/list/list.component.scss index cc4c5428ca..088babff06 100644 --- a/src/frontend/app/shared/components/list/list.component.scss +++ b/src/frontend/app/shared/components/list/list.component.scss @@ -1,9 +1,11 @@ .list-component { - *[hidden], - .sort[hidden], - .filter[hidden], - .add-container[hidden] { - display: none; + *, + .sort, + .filter, + .add-container { + &[hidden] { + display: none; + } } &__blocker { bottom: 0; @@ -16,69 +18,59 @@ &__body-inner { position: relative; } - &__header { - &__left[hidden], - &__right[hidden], - &__top[hidden], - &__bottom[hidden] { - display: none; - } - } $title-left-padding: 25px; $title-content-padding: 10px; &__header { - mat-card-header { - padding: 15px; + position: relative; + mat-progress-bar { + position: absolute; + top: -5px; } - &__left, - &__right, - &__top, - &__bottom { + mat-card { display: flex; flex-direction: row; - align-items: center; - height: 55px; } &__left, &__right { - > div:not(:last-of-type) { - padding-right: $title-content-padding; + align-items: center; + display: flex; + flex: 0; + flex-direction: row; + > div { + &:not(:last-of-type) { + padding-right: $title-content-padding; + } + &:not([hidden]) { + align-items: center; + display: flex; + } } } - &__left .multi-filters mat-form-field { - padding-right: $title-content-padding; - } - &__right { - .add-container { - display: flex; - align-items: center; - } - .mat-paginator { - padding: 0; + &__left { + flex-grow: 99; + &--multi-filters { + mat-form-field { + padding-right: $title-content-padding; + } } } - &__bottom { + &__right { justify-content: flex-end; - align-items: flex-end; } - mat-card { - padding: 0 10px; - mat-card-header { - display: flex; - flex-direction: column; - div:not([hidden]).spacer { - flex-grow: 99; - } - } + & > mat-card { + min-height: 74px; + padding: 0 24px; + width: 100%; } } &__body { - padding-top: 15px; .no-rows { padding: 20px $title-left-padding; } - mat-card[hidden] { - display: none; + mat-card { + &[hidden] { + display: none; + } } } &__paginator { diff --git a/src/frontend/app/shared/components/list/list.component.ts b/src/frontend/app/shared/components/list/list.component.ts index c8fe2db7c6..67147cfccb 100644 --- a/src/frontend/app/shared/components/list/list.component.ts +++ b/src/frontend/app/shared/components/list/list.component.ts @@ -27,6 +27,8 @@ import { IMultiListAction, ListConfig, } from './list.component.types'; +import { combineLatest } from 'rxjs/observable/combineLatest'; +import { map } from 'rxjs/operators'; @Component({ @@ -62,6 +64,9 @@ export class ListComponent implements OnInit, OnDestroy, AfterViewInit { paginationController: IListPaginationController; multiFilterWidgetObservables = new Array(); + isAddingOrSelecting$: Observable; + hasRows$: Observable; + public safeAddForm() { // Something strange is afoot. When using addform in [disabled] it thinks this is null, even when initialised // When applying the question mark (addForm?) it's value is ignored by [disabled] @@ -82,6 +87,15 @@ export class ListComponent implements OnInit, OnDestroy, AfterViewInit { this.dataSource = this.config.getDataSource(); this.multiFilterConfigs = this.config.getMultiFiltersConfigs(); + // Create convenience observables that make the html clearer + this.isAddingOrSelecting$ = combineLatest( + this.dataSource.isAdding$, + this.dataSource.isSelecting$ + ).pipe( + map(([isAdding, isSelecting]) => isAdding || isSelecting) + ); + this.hasRows$ = this.dataSource.pagination$.map(pag => pag.totalResults > 0); + // Set up an observable containing the current view (card/table) const { view, } = getListStateObservables(this.store, this.dataSource.paginationKey); this.view$ = view; From a87163f79b7894894be63acca63c2d930c6034b3 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 13 Feb 2018 16:47:45 +0000 Subject: [PATCH 12/30] Make the header more responsive --- .../components/list/list.component.scss | 4 --- .../components/list/list.component.theme.scss | 29 +++++++++++++++++-- src/frontend/sass/_all-theme.scss | 16 ++-------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/frontend/app/shared/components/list/list.component.scss b/src/frontend/app/shared/components/list/list.component.scss index 088babff06..e020869d75 100644 --- a/src/frontend/app/shared/components/list/list.component.scss +++ b/src/frontend/app/shared/components/list/list.component.scss @@ -28,7 +28,6 @@ } mat-card { display: flex; - flex-direction: row; } &__left, &__right { @@ -54,9 +53,6 @@ } } } - &__right { - justify-content: flex-end; - } & > mat-card { min-height: 74px; padding: 0 24px; diff --git a/src/frontend/app/shared/components/list/list.component.theme.scss b/src/frontend/app/shared/components/list/list.component.theme.scss index 9e7681e42e..56ab0c5e93 100644 --- a/src/frontend/app/shared/components/list/list.component.theme.scss +++ b/src/frontend/app/shared/components/list/list.component.theme.scss @@ -1,9 +1,32 @@ @import '~@angular/material/theming'; -@mixin variables-tab-theme($theme, $app-theme) { +@import '../../../../sass/mixins'; +@mixin list-theme($theme, $app-theme) { $primary: map-get($theme, primary); $foreground-colors: map-get($theme, foreground); $background-colors: map-get($theme, background); - .list-component__header.selected .mat-card { - background-color: mat-color($primary, 50); + .list-component { + &__header { + &.selected { + .mat-card { + background-color: mat-color($primary, 50); + } + } + mat-card { + flex-direction: column; + @include breakpoint(desktop) { + flex-direction: row; + } + @include breakpoint(tablet) { + // background-color: yellow; + // flex-direction: row; + } + } + &__right { + justify-content: flex-start; + @include breakpoint(desktop) { + justify-content: flex-end; + } + } + } } } diff --git a/src/frontend/sass/_all-theme.scss b/src/frontend/sass/_all-theme.scss index a1bb56bbeb..02e946c050 100644 --- a/src/frontend/sass/_all-theme.scss +++ b/src/frontend/sass/_all-theme.scss @@ -47,25 +47,15 @@ $side-nav-light-active: #484848; } html { background-color: $app-background-color; - } - - // App Theme defines a set of colors used by stratos components - $app-theme: ( - app-background-color: $app-background-color, - app-background-text-color: rgba(mat-color($foreground-colors, base), .65), - side-nav: app-generate-nav-theme($theme, $nav-theme), - status: app-generate-status-theme($theme, $status-theme), - subdued-color: $subdued - ); - - // Pass the Material theme and the App Theme to components that need to be themed + } // App Theme defines a set of colors used by stratos components + $app-theme: ( app-background-color: $app-background-color, app-background-text-color: rgba(mat-color($foreground-colors, base), .65), side-nav: app-generate-nav-theme($theme, $nav-theme), status: app-generate-status-theme($theme, $status-theme), subdued-color: $subdued); // Pass the Material theme and the App Theme to components that need to be themed @include dialog-error-theme($theme, $app-theme); @include login-page-theme($theme, $app-theme); @include side-nav-theme($theme, $app-theme); @include dashboard-page-theme($theme, $app-theme); @include display-value-theme($theme, $app-theme); @include steppers-theme($theme, $app-theme); - @include variables-tab-theme($theme, $app-theme); + @include list-theme($theme, $app-theme); @include app-base-page-theme($theme, $app-theme); @include app-page-subheader-theme($theme, $app-theme); @include app-mat-tabs-theme($theme, $app-theme); From ad4d25b9e39b43e49d8f8ae09fdba1d0d18eaa78 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 13 Feb 2018 16:56:28 +0000 Subject: [PATCH 13/30] Lint fixes, fix list text when window width reduced --- .../app/shared/components/list/list.component.html | 6 +++--- .../app/shared/components/list/list.component.scss | 3 +++ .../shared/components/list/list.component.theme.scss | 10 ++-------- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/frontend/app/shared/components/list/list.component.html b/src/frontend/app/shared/components/list/list.component.html index 7ac9bcf22e..503525a46c 100644 --- a/src/frontend/app/shared/components/list/list.component.html +++ b/src/frontend/app/shared/components/list/list.component.html @@ -1,10 +1,10 @@
-
+
-
{{ config.text?.title }}
-
{{dataSource.selectedRows.size}} Selected
+
{{ config.text?.title }}
+
{{dataSource.selectedRows.size}} Selected
diff --git a/src/frontend/app/shared/components/list/list.component.scss b/src/frontend/app/shared/components/list/list.component.scss index e020869d75..029846f3ee 100644 --- a/src/frontend/app/shared/components/list/list.component.scss +++ b/src/frontend/app/shared/components/list/list.component.scss @@ -47,6 +47,9 @@ } &__left { flex-grow: 99; + &--text { + padding: 24px 0; // Valid when window shrunk + } &--multi-filters { mat-form-field { padding-right: $title-content-padding; diff --git a/src/frontend/app/shared/components/list/list.component.theme.scss b/src/frontend/app/shared/components/list/list.component.theme.scss index 56ab0c5e93..750a534718 100644 --- a/src/frontend/app/shared/components/list/list.component.theme.scss +++ b/src/frontend/app/shared/components/list/list.component.theme.scss @@ -6,20 +6,14 @@ $background-colors: map-get($theme, background); .list-component { &__header { - &.selected { - .mat-card { - background-color: mat-color($primary, 50); - } + &--selected { + background-color: mat-color($primary, 50); } mat-card { flex-direction: column; @include breakpoint(desktop) { flex-direction: row; } - @include breakpoint(tablet) { - // background-color: yellow; - // flex-direction: row; - } } &__right { justify-content: flex-start; From 4b4f1e7eb9b21cf80a2b7f559324b01eea97a55e Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 13 Feb 2018 17:01:18 +0000 Subject: [PATCH 14/30] Header colour when selected --- src/frontend/app/shared/components/list/list.component.html | 4 ++-- src/frontend/app/shared/components/list/list.component.scss | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/app/shared/components/list/list.component.html b/src/frontend/app/shared/components/list/list.component.html index 503525a46c..89286febc6 100644 --- a/src/frontend/app/shared/components/list/list.component.html +++ b/src/frontend/app/shared/components/list/list.component.html @@ -1,7 +1,7 @@
-
+
- +
{{ config.text?.title }}
{{dataSource.selectedRows.size}} Selected
diff --git a/src/frontend/app/shared/components/list/list.component.scss b/src/frontend/app/shared/components/list/list.component.scss index 029846f3ee..c181cb261d 100644 --- a/src/frontend/app/shared/components/list/list.component.scss +++ b/src/frontend/app/shared/components/list/list.component.scss @@ -48,7 +48,7 @@ &__left { flex-grow: 99; &--text { - padding: 24px 0; // Valid when window shrunk + padding: 12px 0; // Valid when window shrunk } &--multi-filters { mat-form-field { From 3e20eb06357ef1f2cc7de107e7c009d9787b904f Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 14 Feb 2018 11:20:24 +0000 Subject: [PATCH 15/30] Fix org/space shemas (#1601) * Fix org/space shemas - Selecting cf/org/spaces in filter or add/deploy app should work again - Only have one type of Space/Org Schema * Add missing file --- .../applications/application.service.ts | 15 ++- .../create-application-step3.component.ts | 4 +- .../deploy-application-step3.component.ts | 95 +++++++++---------- .../cf-org-space-service.service.ts | 7 +- .../app/store/actions/action-types.ts | 28 ++++++ .../app/store/actions/application.actions.ts | 4 +- .../app/store/actions/organisation.action.ts | 30 ------ .../app/store/actions/organisation.actions.ts | 52 ++++++++++ .../app/store/actions/organization.actions.ts | 42 -------- .../app/store/actions/space.action.ts | 35 ------- .../app/store/actions/space.actions.ts | 43 ++++++--- 11 files changed, 173 insertions(+), 182 deletions(-) create mode 100644 src/frontend/app/store/actions/action-types.ts delete mode 100644 src/frontend/app/store/actions/organisation.action.ts create mode 100644 src/frontend/app/store/actions/organisation.actions.ts delete mode 100644 src/frontend/app/store/actions/organization.actions.ts delete mode 100644 src/frontend/app/store/actions/space.action.ts diff --git a/src/frontend/app/features/applications/application.service.ts b/src/frontend/app/features/applications/application.service.ts index 33d50f7ade..1267829c4f 100644 --- a/src/frontend/app/features/applications/application.service.ts +++ b/src/frontend/app/features/applications/application.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs/Observable'; -import { map } from 'rxjs/operators'; +import { map, mergeMap } from 'rxjs/operators'; import { EntityService } from '../../core/entity-service'; import { EntityServiceFactory } from '../../core/entity-service-factory.service'; @@ -18,7 +18,6 @@ import { } from '../../store/actions/app-metadata.actions'; import { GetApplication, UpdateApplication, UpdateExistingApplication } from '../../store/actions/application.actions'; import { ApplicationSchema } from '../../store/actions/application.actions'; -import { SpaceSchema } from '../../store/actions/space.action'; import { AppState } from '../../store/app-state'; import { ActionState } from '../../store/reducers/api-request-reducer/types'; import { selectEntity } from '../../store/selectors/api.selectors'; @@ -46,6 +45,7 @@ import { } from './application/application-tabs-base/tabs/build-tab/application-env-vars.service'; import { getRoute, isTCPRoute } from './routes/routes.helper'; import { PaginationMonitor } from '../../shared/monitors/pagination-monitor'; +import { spaceSchemaKey, organisationSchemaKey } from '../../store/actions/action-types'; export interface ApplicationData { fetching: boolean; @@ -157,9 +157,14 @@ export class ApplicationService { .filter(entityInfo => entityInfo.entity && entityInfo.entity.entity && entityInfo.entity.entity.cfGuid) .map(entityInfo => entityInfo.entity.entity) .do(app => { - this.appSpace$ = this.store.select(selectEntity(SpaceSchema.key, app.space_guid)); - // See https://github.com/SUSE/stratos/issues/158 (Failing to populate entity store with a space's org) - this.appOrg$ = this.store.select(selectEntity(SpaceSchema.key, app.space_guid)).map(space => space.entity.organization); + + this.appSpace$ = this.store.select(selectEntity(spaceSchemaKey, app.space_guid)); + this.appOrg$ = this.appSpace$.pipe( + map(space => space.entity.organization_guid), + mergeMap(orgGuid => { + return this.store.select(selectEntity(organisationSchemaKey, orgGuid)); + }) + ); }) .take(1) .subscribe(); diff --git a/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.ts b/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.ts index 8464c650c1..f73bd5d9ed 100644 --- a/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.ts +++ b/src/frontend/app/features/applications/create-application/create-application-step3/create-application-step3.component.ts @@ -18,8 +18,8 @@ import { AppState } from '../../../../store/app-state'; import { selectNewAppState } from '../../../../store/effects/create-app-effects'; import { CreateNewApplicationState } from '../../../../store/types/create-application.types'; import { RouterNav } from '../../../../store/actions/router.actions'; -import { OrganisationSchema } from '../../../../store/actions/organisation.action'; import { RequestInfoState } from '../../../../store/reducers/api-request-reducer/types'; +import { organisationSchemaKey } from '../../../../store/actions/action-types'; @Component({ selector: 'app-create-application-step3', @@ -124,7 +124,7 @@ export class CreateApplicationStep3Component implements OnInit { }) .filter(state => state.cloudFoundryDetails && state.cloudFoundryDetails.org) .mergeMap(state => { - return this.store.select(selectEntity(OrganisationSchema.key, state.cloudFoundryDetails.org)) + return this.store.select(selectEntity(organisationSchemaKey, state.cloudFoundryDetails.org)) .first() .map(org => org.entity.domains); }); diff --git a/src/frontend/app/features/applications/deploy-application/deploy-application-step3/deploy-application-step3.component.ts b/src/frontend/app/features/applications/deploy-application/deploy-application-step3/deploy-application-step3.component.ts index e8653b710d..c0aa14ad44 100644 --- a/src/frontend/app/features/applications/deploy-application/deploy-application-step3/deploy-application-step3.component.ts +++ b/src/frontend/app/features/applications/deploy-application/deploy-application-step3/deploy-application-step3.component.ts @@ -4,9 +4,7 @@ import { Store } from '@ngrx/store'; import { AppState } from '../../../../store/app-state'; import { tap, filter, map, mergeMap, combineLatest, switchMap, share, catchError } from 'rxjs/operators'; import { getEntityById, selectEntity, selectEntities } from '../../../../store/selectors/api.selectors'; -import { OrganizationSchema } from '../../../../store/actions/organization.actions'; import { DeleteDeployAppSection } from '../../../../store/actions/deploy-applications.actions'; -import { SpaceSchema } from '../../../../store/actions/space.actions'; import websocketConnect from 'rxjs-websockets'; import { QueueingSubject } from 'queueing-subject/lib'; import { Subscription } from 'rxjs/Subscription'; @@ -20,6 +18,7 @@ import { RouterNav } from '../../../../store/actions/router.actions'; import { GetAllApplications } from '../../../../store/actions/application.actions'; import { environment } from '../../../../../environments/environment'; import { CfOrgSpaceDataService } from '../../../../shared/data-services/cf-org-space-service.service'; +import { organisationSchemaKey, spaceSchemaKey } from '../../../../store/actions/action-types'; @Component({ selector: 'app-deploy-application-step3', @@ -58,9 +57,9 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { && !!appDetail.applicationSource && !!appDetail.applicationSource.projectName), mergeMap(p => { - const orgSubscription = this.store.select(selectEntity(OrganizationSchema.key, p.cloudFoundryDetails.org)); - const spaceSubscription = this.store.select(selectEntity(SpaceSchema.key, p.cloudFoundryDetails.space)); - return Observable.of(p).combineLatest(orgSubscription, spaceSubscription ); + const orgSubscription = this.store.select(selectEntity(organisationSchemaKey, p.cloudFoundryDetails.org)); + const spaceSubscription = this.store.select(selectEntity(spaceSchemaKey, p.cloudFoundryDetails.space)); + return Observable.of(p).combineLatest(orgSubscription, spaceSubscription); }), tap(p => { const host = window.location.host; @@ -70,10 +69,10 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { `?org=${p[1].entity.name}&space=${p[2].entity.name}` ); - const inputStream = new QueueingSubject(); + const inputStream = new QueueingSubject(); this.messages = websocketConnect(streamUrl, inputStream) - .messages.pipe( - catchError(e => { + .messages.pipe( + catchError(e => { return []; }), share(), @@ -88,21 +87,21 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { this.updateTitle(log); } }), - filter((log ) => log.type === SocketEventTypes.DATA), + filter((log) => log.type === SocketEventTypes.DATA), map((log) => { const timesString = moment(log.timestamp * 1000).format('DD/MM/YYYY hh:mm:ss A'); return ( `${timesString}: ${log.message}` ); }) - ); + ); inputStream.next(this.sendProjectInfo(p[0].applicationSource)); }) ).subscribe(); } - sendProjectInfo = (appSource: DeployApplicationSource) => { + sendProjectInfo = (appSource: DeployApplicationSource) => { if (appSource.type.id === 'git') { if (appSource.type.subType === 'github') { return this.sendGitHubSourceMetadata(appSource); @@ -114,7 +113,7 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { return ''; } - sendGitHubSourceMetadata = (appSource: DeployApplicationSource) => { + sendGitHubSourceMetadata = (appSource: DeployApplicationSource) => { const github = { project: appSource.projectName, branch: appSource.branch.name, @@ -129,7 +128,7 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { return JSON.stringify(msg); } - sendGitUrlSourceMetadata = (appSource: DeployApplicationSource) => { + sendGitUrlSourceMetadata = (appSource: DeployApplicationSource) => { const giturl = { url: appSource.projectName, branch: appSource.branch.name, @@ -155,53 +154,53 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { this.appData.org = this.cfOrgSpaceService.org.select.getValue(); this.appData.space = this.cfOrgSpaceService.space.select.getValue(); break; - case SocketEventTypes.EVENT_PUSH_STARTED : - this.streamTitle = 'Deploying...'; - this.store.dispatch(new GetAllApplications('applicationWall')); - break; - case SocketEventTypes.EVENT_PUSH_COMPLETED : - this.streamTitle = 'Deployed'; - this.apps$ = this.store.select(selectEntities('application')).pipe( - tap(apps => { - Object.values(apps).forEach(app => { - if ( - app.entity.space_guid === this.appData.space && - app.entity.cfGuid === this.appData.cloudFoundry && - app.entity.name === this.appData.Name - ) { - this.appGuid = app.entity.guid; - this.validate = Observable.of(true); - } - }); - }) - ).subscribe(); - break; - case SocketEventTypes.CLOSE_SUCCESS : - this.close(log, null, null, true); - break; + case SocketEventTypes.EVENT_PUSH_STARTED: + this.streamTitle = 'Deploying...'; + this.store.dispatch(new GetAllApplications('applicationWall')); + break; + case SocketEventTypes.EVENT_PUSH_COMPLETED: + this.streamTitle = 'Deployed'; + this.apps$ = this.store.select(selectEntities('application')).pipe( + tap(apps => { + Object.values(apps).forEach(app => { + if ( + app.entity.space_guid === this.appData.space && + app.entity.cfGuid === this.appData.cloudFoundry && + app.entity.name === this.appData.Name + ) { + this.appGuid = app.entity.guid; + this.validate = Observable.of(true); + } + }); + }) + ).subscribe(); + break; + case SocketEventTypes.CLOSE_SUCCESS: + this.close(log, null, null, true); + break; case SocketEventTypes.CLOSE_INVALID_MANIFEST: this.close(log, 'Deploy Failed - Invalid manifest!', - 'Failed to deploy app! Please make sure that a valid manifest.yaml was provided!', true); + 'Failed to deploy app! Please make sure that a valid manifest.yaml was provided!', true); break; case SocketEventTypes.CLOSE_NO_MANIFEST: - this.close(log, 'Deploy Failed - No manifest present!', - 'Failed to deploy app! Please make sure that a valid manifest.yaml is present!', true); + this.close(log, 'Deploy Failed - No manifest present!', + 'Failed to deploy app! Please make sure that a valid manifest.yaml is present!', true); break; case SocketEventTypes.CLOSE_FAILED_CLONE: - this.close(log, 'Deploy Failed - Failed to clone repository!', - 'Failed to deploy app! Please make sure the repository is public!', true); + this.close(log, 'Deploy Failed - Failed to clone repository!', + 'Failed to deploy app! Please make sure the repository is public!', true); break; case SocketEventTypes.CLOSE_FAILED_NO_BRANCH: - this.close(log, 'Deploy Failed - Failed to located branch!', - 'Failed to deploy app! Please make sure that branch exists!', true); + this.close(log, 'Deploy Failed - Failed to located branch!', + 'Failed to deploy app! Please make sure that branch exists!', true); break; case SocketEventTypes.CLOSE_FAILURE: case SocketEventTypes.CLOSE_PUSH_ERROR: case SocketEventTypes.CLOSE_NO_SESSION: case SocketEventTypes.CLOSE_NO_CNSI: case SocketEventTypes.CLOSE_NO_CNSI_USERTOKEN: - this.close(log, 'Deploy Failed!', - 'Failed to deploy app!', true); + this.close(log, 'Deploy Failed!', + 'Failed to deploy app!', true); break; case SocketEventTypes.SOURCE_REQUIRED: case SocketEventTypes.EVENT_CLONED: @@ -209,8 +208,8 @@ export class DeployApplicationStep3Component implements OnInit, OnDestroy { case SocketEventTypes.MANIFEST: break; default: - // noop - } + // noop + } } close(log, title, error, deleteAppSection) { diff --git a/src/frontend/app/shared/data-services/cf-org-space-service.service.ts b/src/frontend/app/shared/data-services/cf-org-space-service.service.ts index b1c80d6657..c4eceaecca 100644 --- a/src/frontend/app/shared/data-services/cf-org-space-service.service.ts +++ b/src/frontend/app/shared/data-services/cf-org-space-service.service.ts @@ -3,12 +3,13 @@ import { Store } from '@ngrx/store'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { Observable } from 'rxjs/Observable'; -import { GetAllOrganizations, OrganizationSchema } from '../../store/actions/organization.actions'; import { AppState } from '../../store/app-state'; import { getPaginationObservables, getCurrentPageRequestInfo } from '../../store/reducers/pagination-reducer/pagination-reducer.helper'; import { endpointsRegisteredEntitiesSelector } from '../../store/selectors/endpoint.selectors'; import { EndpointModel } from '../../store/types/endpoint.types'; import { PaginationMonitorFactory } from '../monitors/pagination-monitor.factory'; +import { GetAllOrganisations } from '../../store/actions/organisation.actions'; +import { OrganisationWithSpaceSchema } from '../../store/actions/action-types'; export interface CfOrgSpaceItem { list$: Observable; @@ -25,7 +26,7 @@ export class CfOrgSpaceDataService { public org: CfOrgSpaceItem; public space: CfOrgSpaceItem; - public paginationAction = new GetAllOrganizations(CfOrgSpaceDataService.CfOrgSpaceServicePaginationKey); + public paginationAction = new GetAllOrganisations(CfOrgSpaceDataService.CfOrgSpaceServicePaginationKey); // TODO: We should optimise this to only fetch the orgs for the current endpoint // (if we inline depth the get orgs request it could be hefty... or we could use a different action to only fetch required data.. @@ -35,7 +36,7 @@ export class CfOrgSpaceDataService { action: this.paginationAction, paginationMonitor: this.paginationMonitorFactory.create( this.paginationAction.paginationKey, - OrganizationSchema + OrganisationWithSpaceSchema ) }); diff --git a/src/frontend/app/store/actions/action-types.ts b/src/frontend/app/store/actions/action-types.ts new file mode 100644 index 0000000000..8c36f51a38 --- /dev/null +++ b/src/frontend/app/store/actions/action-types.ts @@ -0,0 +1,28 @@ +import { schema } from 'normalizr'; +import { getAPIResourceGuid } from '../selectors/api.selectors'; + +export const organisationSchemaKey = 'organization'; +export const OrganisationSchema = new schema.Entity(organisationSchemaKey, {}, { + idAttribute: getAPIResourceGuid +}); + +export const spaceSchemaKey = 'space'; +export const SpaceSchema = new schema.Entity(spaceSchemaKey, {}, { + idAttribute: getAPIResourceGuid +}); + +export const OrganisationWithSpaceSchema = new schema.Entity(organisationSchemaKey, { + entity: { + spaces: [SpaceSchema] + } +}, { + idAttribute: getAPIResourceGuid + }); + +export const SpaceWithOrganisationSchema = new schema.Entity(spaceSchemaKey, { + entity: { + organization: OrganisationSchema + } +}, { + idAttribute: getAPIResourceGuid + }); diff --git a/src/frontend/app/store/actions/application.actions.ts b/src/frontend/app/store/actions/application.actions.ts index 3b2fd53de9..596ddb776e 100644 --- a/src/frontend/app/store/actions/application.actions.ts +++ b/src/frontend/app/store/actions/application.actions.ts @@ -5,7 +5,6 @@ import { Headers, RequestOptions, URLSearchParams } from '@angular/http'; import { schema } from 'normalizr'; import { ApiActionTypes } from './request.actions'; -import { SpaceSchema } from './space.actions'; import { StackSchema } from './stack.action'; import { ActionMergeFunction } from '../types/api.types'; import { PaginatedAction } from '../types/pagination.types'; @@ -14,6 +13,7 @@ import { pick } from '../helpers/reducer.helper'; import { AppMetadataTypes } from './app-metadata.actions'; import { AppStatSchema } from '../types/app-metadata.types'; import { getPaginationKey } from './pagination.actions'; +import { SpaceWithOrganisationSchema } from './action-types'; export const GET_ALL = '[Application] Get all'; export const GET_ALL_SUCCESS = '[Application] Get all success'; @@ -50,7 +50,7 @@ export const DELETE_INSTANCE_FAILED = '[Application Instance] Delete failed'; const ApplicationEntitySchema = { entity: { stack: StackSchema, - space: SpaceSchema + space: SpaceWithOrganisationSchema } }; diff --git a/src/frontend/app/store/actions/organisation.action.ts b/src/frontend/app/store/actions/organisation.action.ts deleted file mode 100644 index b59ddf576a..0000000000 --- a/src/frontend/app/store/actions/organisation.action.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { CFStartAction, IRequestAction, ICFAction } from '../types/request.types'; -import { getAPIResourceGuid } from '../selectors/api.selectors'; -import { schema } from 'normalizr'; -import { ApiActionTypes } from './request.actions'; -import { RequestOptions } from '@angular/http'; - -export const GET = '[Organisation] Get one'; -export const GET_SUCCESS = '[Organisation] Get one success'; -export const GET_FAILED = '[Organisation] Get one failed'; - -export const OrganisationSchema = new schema.Entity('organization', {}, { - idAttribute: getAPIResourceGuid -}); - -export class GetOrganisation extends CFStartAction implements ICFAction { - constructor(public guid: string, public endpointGuid: string) { - super(); - this.options = new RequestOptions(); - this.options.url = `organization/${guid}`; - this.options.method = 'get'; - } - actions = [ - GET, - GET_SUCCESS, - GET_FAILED - ]; - entity = [OrganisationSchema]; - entityKey = OrganisationSchema.key; - options: RequestOptions; -} diff --git a/src/frontend/app/store/actions/organisation.actions.ts b/src/frontend/app/store/actions/organisation.actions.ts new file mode 100644 index 0000000000..b86166eb86 --- /dev/null +++ b/src/frontend/app/store/actions/organisation.actions.ts @@ -0,0 +1,52 @@ +import { RequestOptions } from '@angular/http'; + +import { PaginatedAction } from '../types/pagination.types'; +import { CFStartAction, ICFAction } from '../types/request.types'; +import { OrganisationSchema, organisationSchemaKey, OrganisationWithSpaceSchema } from './action-types'; + +export const GET_ORGANISATION = '[Organisation] Get one'; +export const GET_ORGANISATION_SUCCESS = '[Organisation] Get one success'; +export const GET_ORGANISATION_FAILED = '[Organisation] Get one failed'; + +export const GET_ORGANISATIONS = '[Organization] Get all'; +export const GET_ORGANISATIONS_SUCCESS = '[Organization] Get all success'; +export const GET_ORGANISATIONS_FAILED = '[Organization] Get all failed'; + +export class GetOrganisation extends CFStartAction implements ICFAction { + constructor(public guid: string, public endpointGuid: string) { + super(); + this.options = new RequestOptions(); + this.options.url = `organization/${guid}`; + this.options.method = 'get'; + } + actions = [ + GET_ORGANISATION, + GET_ORGANISATION_SUCCESS, + GET_ORGANISATION_FAILED + ]; + entity = [OrganisationSchema]; + entityKey = organisationSchemaKey; + options: RequestOptions; +} + +export class GetAllOrganisations extends CFStartAction implements PaginatedAction { + constructor(public paginationKey: string) { + super(); + this.options = new RequestOptions(); + this.options.url = 'organizations'; + this.options.method = 'get'; + } + actions = [ + GET_ORGANISATIONS, + GET_ORGANISATIONS_SUCCESS, + GET_ORGANISATIONS_FAILED + ]; + entity = [OrganisationWithSpaceSchema]; + entityKey = organisationSchemaKey; + options: RequestOptions; + initialParams = { + page: 1, + 'results-per-page': 100, + 'inline-relations-depth': 1 + }; +} diff --git a/src/frontend/app/store/actions/organization.actions.ts b/src/frontend/app/store/actions/organization.actions.ts deleted file mode 100644 index d02305b3c1..0000000000 --- a/src/frontend/app/store/actions/organization.actions.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { CFStartAction } from '../types/request.types'; -import { getAPIResourceGuid } from '../selectors/api.selectors'; -import { RequestOptions, URLSearchParams } from '@angular/http'; -import { schema } from 'normalizr'; - -import { ApiActionTypes } from './request.actions'; -import { SpaceSchema } from './space.actions'; -import { PaginatedAction } from '../types/pagination.types'; - -export const GET_ALL = '[Organization] Get all'; -export const GET_ALL_SUCCESS = '[Organization] Get all success'; -export const GET_ALL_FAILED = '[Organization] Get all failed'; - -export const OrganizationSchema = new schema.Entity('organization', { - entity: { - spaces: [SpaceSchema] - } -}, { - idAttribute: getAPIResourceGuid - }); - -export class GetAllOrganizations extends CFStartAction implements PaginatedAction { - constructor(public paginationKey: string) { - super(); - this.options = new RequestOptions(); - this.options.url = 'organizations'; - this.options.method = 'get'; - } - actions = [ - GET_ALL, - GET_ALL_SUCCESS, - GET_ALL_FAILED - ]; - entity = [OrganizationSchema]; - entityKey = OrganizationSchema.key; - options: RequestOptions; - initialParams = { - page: 1, - 'results-per-page': 100, - 'inline-relations-depth': 1 - }; -} diff --git a/src/frontend/app/store/actions/space.action.ts b/src/frontend/app/store/actions/space.action.ts deleted file mode 100644 index 2aaa481675..0000000000 --- a/src/frontend/app/store/actions/space.action.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { CFStartAction, IRequestAction, ICFAction } from '../types/request.types'; -import { getAPIResourceGuid } from '../selectors/api.selectors'; -import { schema } from 'normalizr'; -import { ApiActionTypes } from './request.actions'; -import { RequestOptions } from '@angular/http'; -import { OrganisationSchema } from './organisation.action'; - -export const GET = '[Space] Get one'; -export const GET_SUCCESS = '[Space] Get one success'; -export const GET_FAILED = '[Space] Get one failed'; - -export const SpaceSchema = new schema.Entity('space', { - entity: { - organization: OrganisationSchema - } -}, { - idAttribute: getAPIResourceGuid - }); - -export class GetSpace extends CFStartAction implements ICFAction { - constructor(public guid: string, public endpointGuid: string) { - super(); - this.options = new RequestOptions(); - this.options.url = `space/${guid}`; - this.options.method = 'get'; - } - actions = [ - GET, - GET_SUCCESS, - GET_FAILED - ]; - entity = [SpaceSchema]; - entityKey = SpaceSchema.key; - options: RequestOptions; -} diff --git a/src/frontend/app/store/actions/space.actions.ts b/src/frontend/app/store/actions/space.actions.ts index e108607a98..d6b7aa5d07 100644 --- a/src/frontend/app/store/actions/space.actions.ts +++ b/src/frontend/app/store/actions/space.actions.ts @@ -1,19 +1,32 @@ -import { - CFStartAction, - IRequestAction, - ICFAction -} from '../types/request.types'; -import { getAPIResourceGuid } from '../selectors/api.selectors'; import { RequestOptions, URLSearchParams } from '@angular/http'; -import { schema } from 'normalizr'; -import { ApiActionTypes } from './request.actions'; +import { CFStartAction, ICFAction } from '../types/request.types'; +import { SpaceSchema, spaceSchemaKey, SpaceWithOrganisationSchema } from './action-types'; -export const GET_ALL = '[Space] Get all'; -export const GET_ALL_SUCCESS = '[Space] Get all success'; -export const GET_ALL_FAILED = '[Space] Get all failed'; +export const GET_SPACES = '[Space] Get all'; +export const GET_SPACES_SUCCESS = '[Space] Get all success'; +export const GET_SPACES_FAILED = '[Space] Get all failed'; -export const SpaceSchema = new schema.Entity('space'); +export const GET_SPACE = '[Space] Get one'; +export const GET_SPACE_SUCCESS = '[Space] Get one success'; +export const GET_SPACE_FAILED = '[Space] Get one failed'; + +export class GetSpace extends CFStartAction implements ICFAction { + constructor(public guid: string, public endpointGuid: string) { + super(); + this.options = new RequestOptions(); + this.options.url = `space/${guid}`; + this.options.method = 'get'; + } + actions = [ + GET_SPACE, + GET_SPACE_SUCCESS, + GET_SPACE_FAILED + ]; + entity = [SpaceSchema]; + entityKey = spaceSchemaKey; + options: RequestOptions; +} export class GetAllSpaces extends CFStartAction implements ICFAction { constructor(public paginationKey?: string) { @@ -26,8 +39,8 @@ export class GetAllSpaces extends CFStartAction implements ICFAction { this.options.params.set('results-per-page', '100'); this.options.params.set('inline-relations-depth', '1'); } - actions = [GET_ALL, GET_ALL_SUCCESS, GET_ALL_FAILED]; - entity = [SpaceSchema]; - entityKey = SpaceSchema.key; + actions = [GET_SPACES, GET_SPACES_SUCCESS, GET_SPACES_FAILED]; + entity = [SpaceWithOrganisationSchema]; + entityKey = spaceSchemaKey; options: RequestOptions; } From 257f14dfa5526a34bce9452c9ed5cb4ded2d1033 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Thu, 15 Feb 2018 13:00:35 +0000 Subject: [PATCH 16/30] Fixing application and endpoints view when no endpoints are attached to the appliction --- .../list/data-sources-controllers/list-data-source.ts | 6 ++++-- src/frontend/app/shared/monitors/pagination-monitor.ts | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source.ts b/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source.ts index fd4ff7a8a4..7c1aa1b971 100644 --- a/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source.ts +++ b/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source.ts @@ -244,7 +244,9 @@ export abstract class ListDataSource extends DataSource implements ).pipe( filter(([paginationEntity, entities]) => !getCurrentPageRequestInfo(paginationEntity).busy), map(([paginationEntity, entities]) => { - + if (entities && !entities.length) { + return []; + } const entitiesPreFilter = entities.length; if (dataFunctions && dataFunctions.length) { entities = dataFunctions.reduce((value, fn) => { @@ -268,7 +270,7 @@ export abstract class ListDataSource extends DataSource implements publishReplay(1), refCount(), tag('local-list') - ); + ); } getPaginationCompareString(paginationEntity: PaginationEntityState) { diff --git a/src/frontend/app/shared/monitors/pagination-monitor.ts b/src/frontend/app/shared/monitors/pagination-monitor.ts index 32f20325f2..958137025b 100644 --- a/src/frontend/app/shared/monitors/pagination-monitor.ts +++ b/src/frontend/app/shared/monitors/pagination-monitor.ts @@ -114,10 +114,10 @@ export class PaginationMonitor { withLatestFrom(this.store.select(getAPIRequestDataState)), map(([[pagination, entities], allEntities]) => { const page = pagination.ids[pagination.currentPage] || []; - return page.length ? denormalize(page, [schema], allEntities).filter(ent => !!ent) : null; + return page.length ? denormalize(page, [schema], allEntities).filter(ent => !!ent) : []; }), shareReplay(1) - ); + ); } private createErrorObservable(pagination$: Observable) { From 0913ee0d34e3abcfa6e360e0bc61ad2e296bc0ed Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 15 Feb 2018 13:19:10 +0000 Subject: [PATCH 17/30] Fix standupdevenv --- deploy/db/scripts/development.sh | 2 +- deploy/stand-up-dev-env.sh | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/deploy/db/scripts/development.sh b/deploy/db/scripts/development.sh index 615da17bae..d8b03ec927 100755 --- a/deploy/db/scripts/development.sh +++ b/deploy/db/scripts/development.sh @@ -20,7 +20,7 @@ echo "Checking database status." # Run migrations echo "Attempting database migrations." -./portal-proxy--env=mariadb-development up +./portal-proxy --env=mariadb-development up # CHeck the status echo "Checking database status." diff --git a/deploy/stand-up-dev-env.sh b/deploy/stand-up-dev-env.sh index 4c1b76432d..407dea5fbf 100755 --- a/deploy/stand-up-dev-env.sh +++ b/deploy/stand-up-dev-env.sh @@ -71,6 +71,13 @@ function uaa_downloads { ./uaa/prepare.sh } +function dev_certs { + CERTS_PATH="${PROG_DIR}/../dev-certs" + if [ ! -d "${CERTS_PATH}" ]; then + CERTS_PATH=${CERTS_PATH} ./tools/generate_cert.sh + fi +} + function build { echo "===== Building the portal proxy" export USER_ID=$(id -u) @@ -131,6 +138,7 @@ if [ "$CLEAN" = true ] ; then clean fi uaa_downloads +dev_certs build docker ps popd From a9415f10cedca549e4cba2d9a3ba43c85abccd3e Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 15 Feb 2018 13:33:05 +0000 Subject: [PATCH 18/30] Move migration file to correct dir --- .../backend/app-core/datastore}/20171108102900_AuthType.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {deploy/db/migrations => src/backend/app-core/datastore}/20171108102900_AuthType.go (91%) diff --git a/deploy/db/migrations/20171108102900_AuthType.go b/src/backend/app-core/datastore/20171108102900_AuthType.go similarity index 91% rename from deploy/db/migrations/20171108102900_AuthType.go rename to src/backend/app-core/datastore/20171108102900_AuthType.go index 7ce1609602..58d4411b67 100644 --- a/deploy/db/migrations/20171108102900_AuthType.go +++ b/src/backend/app-core/datastore/20171108102900_AuthType.go @@ -5,7 +5,7 @@ import ( "fmt" ) -func Up_20171108102900(txn *sql.Tx) { +func (s *StratosMigrations) Up_20171108102900(txn *sql.Tx) { createTokens := "ALTER TABLE tokens " createTokens += "ADD auth_type VARCHAR(255) DEFAULT \"OAuth2\", " From 532c64bb91494bf702fc89b993e88424ef03db1e Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 15 Feb 2018 14:59:37 +0000 Subject: [PATCH 19/30] Fix package name --- src/backend/app-core/datastore/20171108102900_AuthType.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/app-core/datastore/20171108102900_AuthType.go b/src/backend/app-core/datastore/20171108102900_AuthType.go index 58d4411b67..51291edd99 100644 --- a/src/backend/app-core/datastore/20171108102900_AuthType.go +++ b/src/backend/app-core/datastore/20171108102900_AuthType.go @@ -1,4 +1,4 @@ -package main +package datastore import ( "database/sql" From 4df4246e9268a95096f432337a53d99d6c92b05e Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 15 Feb 2018 15:47:24 +0000 Subject: [PATCH 20/30] Fix schema --- .../app-core/datastore/20171108102900_AuthType.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/backend/app-core/datastore/20171108102900_AuthType.go b/src/backend/app-core/datastore/20171108102900_AuthType.go index 51291edd99..fff38d3332 100644 --- a/src/backend/app-core/datastore/20171108102900_AuthType.go +++ b/src/backend/app-core/datastore/20171108102900_AuthType.go @@ -7,15 +7,17 @@ import ( func (s *StratosMigrations) Up_20171108102900(txn *sql.Tx) { - createTokens := "ALTER TABLE tokens " - createTokens += "ADD auth_type VARCHAR(255) DEFAULT \"OAuth2\", " - createTokens += "ADD meta_data TEXT " - createTokens += ";" - + createTokens := "ALTER TABLE tokens ADD auth_type VARCHAR(255) DEFAULT \"OAuth2\"" _, err := txn.Exec(createTokens) if err != nil { fmt.Printf("Failed to migrate due to: %v", err) } + + createTokens = "ALTER TABLE tokens ADD meta_data TEXT" + _, err = txn.Exec(createTokens) + if err != nil { + fmt.Printf("Failed to migrate due to: %v", err) + } } func Down_20171108102900(txn *sql.Tx) { From 51fd317b993e07ba1530d93e7cf5547533fadca8 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 12 Feb 2018 15:42:11 +0000 Subject: [PATCH 21/30] Show application page promptly - Don't wait for summary (used for total no. of services and routes) - Ensure stats show as soon as possible --- .../applications/application.service.ts | 11 +++----- .../application-tabs-base.component.html | 26 +++++-------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/src/frontend/app/features/applications/application.service.ts b/src/frontend/app/features/applications/application.service.ts index 1944002e79..3759abc18f 100644 --- a/src/frontend/app/features/applications/application.service.ts +++ b/src/frontend/app/features/applications/application.service.ts @@ -230,13 +230,13 @@ export class ApplicationService { }).shareReplay(1); this.applicationState$ = this.waitForAppEntity$ - .withLatestFrom(this.appStats$) + .combineLatest(this.appStats$) .map(([appInfo, appStatsArray]: [EntityInfo, APIResource[]]) => { return this.appStateService.get(appInfo.entity.entity, appStatsArray.map(apiResource => apiResource.entity)); }).shareReplay(1); this.applicationInstanceState$ = this.waitForAppEntity$ - .withLatestFrom(this.appStats$) + .combineLatest(this.appStats$) .switchMap(([appInfo, appStatsArray]: [EntityInfo, APIResource[]]) => { return ApplicationService.getApplicationState(this.store, this.appStateService, appInfo.entity.entity, this.appGuid, this.cfGuid); }).shareReplay(1); @@ -250,11 +250,8 @@ export class ApplicationService { /** * An observable based on the core application entity */ - this.isFetchingApp$ = Observable.combineLatest( - this.app$.map(ei => ei.entityRequestInfo.fetching), - this.appSummary$.map(as => as.entityRequestInfo.fetching) - ) - .map((fetching) => fetching[0] || fetching[1]).shareReplay(1); + this.isFetchingApp$ = this.appEntityService.isFetchingEntity$; + this.isUpdatingApp$ = this.app$.map(a => { diff --git a/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.html b/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.html index cf6f35b141..0a18f748b6 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.html +++ b/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.html @@ -1,28 +1,17 @@

{{ (applicationService.application$ | async)?.app.entity.name }}

- - - - - - - launch - - - - + -
From d4c886ee2d4845b4d775315cc6099f59fb68299d Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 15 Feb 2018 14:01:58 +0000 Subject: [PATCH 22/30] Ensure we always have an initial app state on app summary page --- .../features/applications/application.service.ts | 13 +++---------- .../card-app-instances.component.html | 2 +- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/frontend/app/features/applications/application.service.ts b/src/frontend/app/features/applications/application.service.ts index 3759abc18f..2cc76dd948 100644 --- a/src/frontend/app/features/applications/application.service.ts +++ b/src/frontend/app/features/applications/application.service.ts @@ -57,7 +57,6 @@ export interface ApplicationData { @Injectable() export class ApplicationService { - applicationInstanceState$: Observable; private appEntityService: EntityService; private appSummaryEntityService: EntityService; @@ -214,7 +213,7 @@ export class ApplicationService { this.application$ = this.waitForAppEntity$ .combineLatest( - this.store.select(endpointEntitiesSelector), + this.store.select(endpointEntitiesSelector), ) .filter(([{ entity, entityRequestInfo }, endpoints]: [EntityInfo, any]) => { return entity && entity.entity && entity.entity.cfGuid; @@ -230,15 +229,9 @@ export class ApplicationService { }).shareReplay(1); this.applicationState$ = this.waitForAppEntity$ - .combineLatest(this.appStats$) + .combineLatest(this.appStats$.startWith(null)) .map(([appInfo, appStatsArray]: [EntityInfo, APIResource[]]) => { - return this.appStateService.get(appInfo.entity.entity, appStatsArray.map(apiResource => apiResource.entity)); - }).shareReplay(1); - - this.applicationInstanceState$ = this.waitForAppEntity$ - .combineLatest(this.appStats$) - .switchMap(([appInfo, appStatsArray]: [EntityInfo, APIResource[]]) => { - return ApplicationService.getApplicationState(this.store, this.appStateService, appInfo.entity.entity, this.appGuid, this.cfGuid); + return this.appStateService.get(appInfo.entity.entity, appStatsArray ? appStatsArray.map(apiResource => apiResource.entity) : null); }).shareReplay(1); this.applicationStratProject$ = this.appEnvVars.entities$.map(applicationEnvVars => { diff --git a/src/frontend/app/shared/components/cards/card-app-instances/card-app-instances.component.html b/src/frontend/app/shared/components/cards/card-app-instances/card-app-instances.component.html index 526d47b0ce..28f4af150a 100644 --- a/src/frontend/app/shared/components/cards/card-app-instances/card-app-instances.component.html +++ b/src/frontend/app/shared/components/cards/card-app-instances/card-app-instances.component.html @@ -1,5 +1,5 @@ - + Instances From c5ad9715c382add92e89d24f5aa918811b912b8e Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Thu, 15 Feb 2018 15:14:19 +0000 Subject: [PATCH 23/30] Fetch app stats concurrent to application fetch, show uptime block on offline apps and don't poll app status or summary for offline apps --- src/frontend/app/core/utils.service.ts | 74 +++++++++---------- .../application-monitor.service.ts | 2 +- .../applications/application.service.ts | 19 +---- .../application-tabs-base.component.ts | 11 ++- .../card-app-uptime.component.html | 10 +-- .../card-app-uptime.component.ts | 28 +++++-- src/frontend/app/shared/pipes/uptime.pipe.ts | 5 +- 7 files changed, 80 insertions(+), 69 deletions(-) diff --git a/src/frontend/app/core/utils.service.ts b/src/frontend/app/core/utils.service.ts index bcb348e772..a83d6ed7d4 100644 --- a/src/frontend/app/core/utils.service.ts +++ b/src/frontend/app/core/utils.service.ts @@ -12,41 +12,41 @@ export class UtilsService { * */ public urlValidationExpression = - '^' + - // protocol identifier - 'http(s)?://' + - // user:pass authentication - '(?:\\S+(?::\\S*)?@)?' + - '(?:' + - // IP address exclusion - // private & local networks - '(?!(?:10|127)(?:\\.\\d{1,3}){3})' + - '(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})' + - '(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})' + - // IP address dotted notation octets - // excludes loopback network 0.0.0.0 - // excludes reserved space >= 224.0.0.0 - // excludes network & broadcast addresses - // (first & last IP address of each class) - '(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])' + - '(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}' + - '(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))' + - '|' + - // host name - '(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)' + - // domain name - '(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*' + - // TLD identifier - '(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))' + - // TLD may end with dot - '\\.?' + - ')' + - // port number - '(?::\\d{2,5})?' + - // resource path - '(?:[/?#]\\S*)?' + - '$' - ; + '^' + + // protocol identifier + 'http(s)?://' + + // user:pass authentication + '(?:\\S+(?::\\S*)?@)?' + + '(?:' + + // IP address exclusion + // private & local networks + '(?!(?:10|127)(?:\\.\\d{1,3}){3})' + + '(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})' + + '(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})' + + // IP address dotted notation octets + // excludes loopback network 0.0.0.0 + // excludes reserved space >= 224.0.0.0 + // excludes network & broadcast addresses + // (first & last IP address of each class) + '(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])' + + '(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}' + + '(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))' + + '|' + + // host name + '(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)' + + // domain name + '(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*' + + // TLD identifier + '(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))' + + // TLD may end with dot + '\\.?' + + ')' + + // port number + '(?::\\d{2,5})?' + + // resource path + '(?:[/?#]\\S*)?' + + '$' + ; constructor() { } @@ -146,14 +146,14 @@ export class UtilsService { const hours = Math.floor(uptime / 3600); uptime = uptime % 3600; const minutes = Math.floor(uptime / 60); - const seconds = uptime % 60; + const seconds = uptime % 60; return ( this.formatPart(days, 'd', 'd') + this.formatPart(hours, 'h', 'h') + this.formatPart(minutes, 'm', 'm') + this.formatPart(seconds, 's', 's') - .trim() + .trim() ); } diff --git a/src/frontend/app/features/applications/application-monitor.service.ts b/src/frontend/app/features/applications/application-monitor.service.ts index 9b41ad0419..5088fa1e81 100644 --- a/src/frontend/app/features/applications/application-monitor.service.ts +++ b/src/frontend/app/features/applications/application-monitor.service.ts @@ -63,7 +63,7 @@ export class AppMonitorState { @Injectable() export class ApplicationMonitorService { - appMonitor$: Observable; + appMonitor$: Observable; constructor( private applicationService: ApplicationService, diff --git a/src/frontend/app/features/applications/application.service.ts b/src/frontend/app/features/applications/application.service.ts index 2cc76dd948..7db2534a15 100644 --- a/src/frontend/app/features/applications/application.service.ts +++ b/src/frontend/app/features/applications/application.service.ts @@ -194,22 +194,11 @@ export class ApplicationService { AppStatSchema ) }, true); + // This will fail to fetch the app stats if the current app is not running but we're + // willing to do this to speed up the initial fetch for a running application. + this.appStats$ = appStats.entities$; - this.appStats$ = this.waitForAppEntity$ - .filter(ai => ai && ai.entity && ai.entity.entity) - .switchMap(ai => { - if (ai.entity.entity.state === 'STARTED') { - return appStats.entities$; - } else { - return Observable.of(new Array>()); - } - }); - - this.appStatsFetching$ = this.waitForAppEntity$ - .filter(ai => ai && ai.entity && ai.entity.entity && ai.entity.entity.state === 'STARTED') - .switchMap(ai => { - return appStats.pagination$; - }).shareReplay(1); + this.appStatsFetching$ = appStats.pagination$.shareReplay(1); this.application$ = this.waitForAppEntity$ .combineLatest( diff --git a/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts b/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts index bb9fb88c16..cf0a38279d 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts +++ b/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts @@ -12,6 +12,7 @@ import { DeleteApplication } from '../../../../store/actions/application.actions import { RouterNav } from '../../../../store/actions/router.actions'; import { AppState } from '../../../../store/app-state'; import { ApplicationService } from '../../application.service'; +import { APIResource } from '../../../../store/types/api.types'; // Confirmation dialogs const appStopConfirmation = new ConfirmationDialog( @@ -42,7 +43,7 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { private route: ActivatedRoute, private router: Router, private applicationService: ApplicationService, - private entityService: EntityService, + private entityService: EntityService, private store: Store, private confirmDialog: ConfirmationDialogService ) { } @@ -113,9 +114,11 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { // Auto refresh this.entityServiceAppRefresh$ = this.entityService .poll(10000, this.autoRefreshString) - .do(() => { - this.store.dispatch(new GetAppSummaryAction(appGuid, cfGuid)); - this.store.dispatch(new GetAppStatsAction(appGuid, cfGuid)); + .do(({ resource }) => { + if (resource && resource.entity && resource.entity.state === 'STARTED') { + this.store.dispatch(new GetAppSummaryAction(appGuid, cfGuid)); + this.store.dispatch(new GetAppStatsAction(appGuid, cfGuid)); + } }) .subscribe(); diff --git a/src/frontend/app/shared/components/cards/card-app-uptime/card-app-uptime.component.html b/src/frontend/app/shared/components/cards/card-app-uptime/card-app-uptime.component.html index daed8f9887..bdd6549972 100644 --- a/src/frontend/app/shared/components/cards/card-app-uptime/card-app-uptime.component.html +++ b/src/frontend/app/shared/components/cards/card-app-uptime/card-app-uptime.component.html @@ -5,12 +5,12 @@ Uptime -
{{ appData.monitor.max.uptime | uptime }}
+
{{ appData.maxUptime | uptime }}
-
-
- {{ appData.monitor.avg.uptime | uptime }} - {{ appData.monitor.min.uptime | uptime }} +
+
+ {{ appData.averageUptime | uptime }} + {{ appData.minUptime | uptime }}
diff --git a/src/frontend/app/shared/components/cards/card-app-uptime/card-app-uptime.component.ts b/src/frontend/app/shared/components/cards/card-app-uptime/card-app-uptime.component.ts index b00fd924a1..66e6132d7e 100644 --- a/src/frontend/app/shared/components/cards/card-app-uptime/card-app-uptime.component.ts +++ b/src/frontend/app/shared/components/cards/card-app-uptime/card-app-uptime.component.ts @@ -1,7 +1,9 @@ import { Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; +import { map, startWith } from 'rxjs/operators'; + import { ApplicationMonitorService } from '../../../../features/applications/application-monitor.service'; import { ApplicationService } from '../../../../features/applications/application.service'; -import { Observable } from 'rxjs/Observable'; @Component({ selector: 'app-card-app-uptime', @@ -12,13 +14,27 @@ export class CardAppUptimeComponent implements OnInit { constructor(private appService: ApplicationService, private appMonitor: ApplicationMonitorService) { } - appData$: Observable; + appData$: Observable<{ + maxUptime: number, + minUptime: number, + averageUptime: number, + runningCount: number + }>; ngOnInit() { - this.appData$ = Observable.combineLatest( - this.appMonitor.appMonitor$, - this.appService.application$.map(data => data.app.entity.state === 'STARTED'), - (monitor, isRunning) => ({ monitor: monitor, isRunning: isRunning, status: !isRunning ? 'tentative' : monitor.status.usage }) + this.appData$ = this.appMonitor.appMonitor$.pipe( + map(monitor => ({ + maxUptime: monitor.max.uptime, + minUptime: monitor.min.uptime, + averageUptime: monitor.avg.uptime, + runningCount: monitor.running + })), + startWith({ + maxUptime: 0, + minUptime: 0, + averageUptime: 0, + runningCount: 0 + }) ); } } diff --git a/src/frontend/app/shared/pipes/uptime.pipe.ts b/src/frontend/app/shared/pipes/uptime.pipe.ts index 55ee425443..aac893b611 100644 --- a/src/frontend/app/shared/pipes/uptime.pipe.ts +++ b/src/frontend/app/shared/pipes/uptime.pipe.ts @@ -7,9 +7,12 @@ import { UtilsService } from '../../core/utils.service'; }) export class UptimePipe implements PipeTransform { - constructor(private utils: UtilsService) {} + constructor(private utils: UtilsService) { } transform(uptime): string { + if (uptime === 'offline') { + return 'Offline'; + } return this.utils.formatUptime(uptime); } } From a9d6ab99fd3403cf42bfbf33572d345746cbfdf6 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 15 Feb 2018 15:22:49 +0000 Subject: [PATCH 24/30] Only gate stats polling --- .../application-tabs-base/application-tabs-base.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts b/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts index cf0a38279d..759d5b42c8 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts +++ b/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts @@ -115,8 +115,8 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { this.entityServiceAppRefresh$ = this.entityService .poll(10000, this.autoRefreshString) .do(({ resource }) => { + this.store.dispatch(new GetAppSummaryAction(appGuid, cfGuid)); if (resource && resource.entity && resource.entity.state === 'STARTED') { - this.store.dispatch(new GetAppSummaryAction(appGuid, cfGuid)); this.store.dispatch(new GetAppStatsAction(appGuid, cfGuid)); } }) From 2cae535a38b1a979a422e62c7cee570701c8dc88 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 15 Feb 2018 16:11:36 +0000 Subject: [PATCH 25/30] Fix column count mismatch --- src/backend/app-core/repository/tokens/pgsql_tokens.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backend/app-core/repository/tokens/pgsql_tokens.go b/src/backend/app-core/repository/tokens/pgsql_tokens.go index cad032e27f..4a8c15aa5b 100644 --- a/src/backend/app-core/repository/tokens/pgsql_tokens.go +++ b/src/backend/app-core/repository/tokens/pgsql_tokens.go @@ -46,8 +46,8 @@ var insertCNSIToken = `INSERT INTO tokens (cnsi_guid, user_guid, token_type, aut VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)` var updateCNSIToken = `UPDATE tokens - SET auth_token = $1, refresh_token = $2, token_expiry = $3, disconnected = $4 - WHERE cnsi_guid = $5 AND user_guid = $6 AND token_type = $7 AND auth_type = $7 AND meta_data = $8` + SET auth_token = $1, refresh_token = $2, token_expiry = $3, disconnected = $4, meta_data = $5, + WHERE cnsi_guid = $6 AND user_guid = $7 AND token_type = $8 AND auth_type = $9` var deleteCNSIToken = `DELETE FROM tokens WHERE token_type = 'cnsi' AND cnsi_guid = $1 AND user_guid = $2` @@ -238,7 +238,7 @@ func (p *PgsqlTokenRepository) SaveCNSIToken(cnsiGUID string, userGUID string, t case 0: if _, insertErr := p.db.Exec(insertCNSIToken, cnsiGUID, userGUID, "cnsi", ciphertextAuthToken, - ciphertextRefreshToken, tr.TokenExpiry, tr.Disconnected, tr.AuthType, tr.AuthType, tr.Metadata); insertErr != nil { + ciphertextRefreshToken, tr.TokenExpiry, tr.Disconnected, tr.AuthType, tr.Metadata); insertErr != nil { msg := "Unable to INSERT CNSI token: %v" log.Printf(msg, insertErr) @@ -251,7 +251,7 @@ func (p *PgsqlTokenRepository) SaveCNSIToken(cnsiGUID string, userGUID string, t log.Println("Existing CNSI token found - attempting update.") result, err := p.db.Exec(updateCNSIToken, ciphertextAuthToken, ciphertextRefreshToken, tr.TokenExpiry, - tr.Disconnected, cnsiGUID, userGUID, "cnsi", tr.AuthType, tr.Metadata) + tr.Disconnected, tr.Metadata, cnsiGUID, userGUID, "cnsi", tr.AuthType) if err != nil { msg := "Unable to UPDATE CNSI token: %v" log.Printf(msg, err) From 063be6f2b958ba5ab112f4523bb7c157a08d7e22 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Thu, 15 Feb 2018 16:23:26 +0000 Subject: [PATCH 26/30] Styling tweeks --- .../list/list-cards/cards.component.scss | 1 - .../components/list/list.component.html | 23 +++++++++---------- .../components/list/list.component.scss | 23 ++++++++++++++++--- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/src/frontend/app/shared/components/list/list-cards/cards.component.scss b/src/frontend/app/shared/components/list/list-cards/cards.component.scss index 1a30ec14da..118ab7a93e 100644 --- a/src/frontend/app/shared/components/list/list-cards/cards.component.scss +++ b/src/frontend/app/shared/components/list/list-cards/cards.component.scss @@ -2,7 +2,6 @@ display: flex; flex-flow: wrap; justify-content: space-between; - padding-top: 20px; .mat-card { &::after { bottom: 0; diff --git a/src/frontend/app/shared/components/list/list.component.html b/src/frontend/app/shared/components/list/list.component.html index 89286febc6..457152e11e 100644 --- a/src/frontend/app/shared/components/list/list.component.html +++ b/src/frontend/app/shared/components/list/list.component.html @@ -1,7 +1,7 @@ -
+
- +
{{ config.text?.title }}
{{dataSource.selectedRows.size}} Selected
@@ -26,20 +26,20 @@
-
- - - +
+ {{column.headerCell()}} + +
@@ -87,7 +87,6 @@
-
@@ -98,7 +97,7 @@ - +
There are no entries.
diff --git a/src/frontend/app/shared/components/list/list.component.scss b/src/frontend/app/shared/components/list/list.component.scss index c181cb261d..f4791ad9e8 100644 --- a/src/frontend/app/shared/components/list/list.component.scss +++ b/src/frontend/app/shared/components/list/list.component.scss @@ -7,6 +7,21 @@ display: none; } } + &__cards { + .list-component__header-card { + margin-bottom: 20px; + } + } + &__table { + .list-component__header-card { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + } + .list-component__paginator { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + } &__blocker { bottom: 0; left: 0; @@ -29,11 +44,13 @@ mat-card { display: flex; } + &-card { + justify-content: space-between; + } &__left, &__right { align-items: center; display: flex; - flex: 0; flex-direction: row; > div { &:not(:last-of-type) { @@ -46,9 +63,9 @@ } } &__left { - flex-grow: 99; + flex: 1; &--text { - padding: 12px 0; // Valid when window shrunk + padding: 10px; } &--multi-filters { mat-form-field { From e58332126f6badb9c7bd40801e51e38deb06d207 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Thu, 15 Feb 2018 16:29:17 +0000 Subject: [PATCH 27/30] CC issues? --- src/frontend/app/shared/components/list/list.component.scss | 2 +- src/frontend/sass/_all-theme.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/app/shared/components/list/list.component.scss b/src/frontend/app/shared/components/list/list.component.scss index f4791ad9e8..14e9a58caf 100644 --- a/src/frontend/app/shared/components/list/list.component.scss +++ b/src/frontend/app/shared/components/list/list.component.scss @@ -73,7 +73,7 @@ } } } - & > mat-card { + > mat-card { min-height: 74px; padding: 0 24px; width: 100%; diff --git a/src/frontend/sass/_all-theme.scss b/src/frontend/sass/_all-theme.scss index 02e946c050..dfb2597c9b 100644 --- a/src/frontend/sass/_all-theme.scss +++ b/src/frontend/sass/_all-theme.scss @@ -48,7 +48,7 @@ $side-nav-light-active: #484848; html { background-color: $app-background-color; } // App Theme defines a set of colors used by stratos components - $app-theme: ( app-background-color: $app-background-color, app-background-text-color: rgba(mat-color($foreground-colors, base), .65), side-nav: app-generate-nav-theme($theme, $nav-theme), status: app-generate-status-theme($theme, $status-theme), subdued-color: $subdued); // Pass the Material theme and the App Theme to components that need to be themed + $app-theme: (app-background-color: $app-background-color, app-background-text-color: rgba(mat-color($foreground-colors, base), .65), side-nav: app-generate-nav-theme($theme, $nav-theme), status: app-generate-status-theme($theme, $status-theme), subdued-color: $subdued); // Pass the Material theme and the App Theme to components that need to be themed @include dialog-error-theme($theme, $app-theme); @include login-page-theme($theme, $app-theme); @include side-nav-theme($theme, $app-theme); From 2ea711a22b47a01eb1b9e5b2405183c33b1afd8a Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Thu, 15 Feb 2018 16:50:08 +0000 Subject: [PATCH 28/30] Fix enum issue --- src/backend/app-core/auth.go | 10 +++++----- src/backend/app-core/repository/interfaces/structs.go | 8 +++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/backend/app-core/auth.go b/src/backend/app-core/auth.go index 0740e2efd0..9589bc93cd 100644 --- a/src/backend/app-core/auth.go +++ b/src/backend/app-core/auth.go @@ -212,19 +212,19 @@ func (p *portalProxy) fetchToken(cnsiGUID string, c echo.Context) (*UAAResponse, } authTypeStr := c.FormValue("auth") - authType := interfaces.OAuth2 + authType := interfaces.AuthTypeOAuth2 switch authTypeStr { case "http": - authType = interfaces.HttpBasic + authType = interfaces.AuthTypeHttpBasic default: - authType = interfaces.OAuth2 + authType = interfaces.AuthTypeOAuth2 } - if authType == interfaces.OAuth2 { + if authType == interfaces.AuthTypeOAuth2 { return p.fetchOAuth2Token(cnsiRecord, c) } - if authType == interfaces.HttpBasic { + if authType == interfaces.AuthTypeHttpBasic { return p.fetchHttpBasicToken(cnsiRecord, c) } diff --git a/src/backend/app-core/repository/interfaces/structs.go b/src/backend/app-core/repository/interfaces/structs.go index 521c1b9b8e..a9ea833bc4 100644 --- a/src/backend/app-core/repository/interfaces/structs.go +++ b/src/backend/app-core/repository/interfaces/structs.go @@ -31,11 +31,9 @@ type CNSIRecord struct { SkipSSLValidation bool `json:"skip_ssl_validation"` } -type AuthType string - const ( - OAuth2 AuthType = "OAuth2" - HttpBasic AuthType = "HttpBasic" + AuthTypeOAuth2 = "OAuth2" + AuthTypeHttpBasic = "HttpBasic" ) //TODO this could be moved back to tokens subpackage, and extensions could import it? @@ -44,7 +42,7 @@ type TokenRecord struct { RefreshToken string TokenExpiry int64 Disconnected bool - AuthType AuthType + AuthType string Metadata string } From 79524625561c23be86b7addcd971707eab160b6b Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 15 Feb 2018 17:10:57 +0000 Subject: [PATCH 29/30] Change name of backend builder image for v2 (#1585) * Docker compose changes for V2 * Update Stratos UI references in docs and changes to get all-in-one deployment working * Uncomment testing * Remove blank lines at end of file * Change name of backend builder for v2 * Update .gitignore * Update .gitignore * Update .gitignore * Fix image name --- .gitignore | 2 +- deploy/build_portal_proxy.sh | 4 ++-- deploy/ci/tasks/stratos-ui/prep-proxy-image.yml | 4 ++-- deploy/docker-compose/build.sh | 4 ++-- deploy/kubernetes/build.sh | 12 ++++++------ deploy/stratos-base-images/build-base-images.sh | 2 +- deploy/tools/build-push-proxy-builder-image.sh | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 01c8401c1a..10644b6a97 100644 --- a/.gitignore +++ b/.gitignore @@ -80,4 +80,4 @@ deploy/ci/travis/temp/ deploy/glide-cache/ .dist/ -deploy/uaa/tmp/ \ No newline at end of file +deploy/uaa/tmp/ diff --git a/deploy/build_portal_proxy.sh b/deploy/build_portal_proxy.sh index 2a2b8fb3b8..7404455b32 100755 --- a/deploy/build_portal_proxy.sh +++ b/deploy/build_portal_proxy.sh @@ -17,9 +17,9 @@ docker run -it \ -e USER_ID=$(id -u) \ -e GROUP_ID=$(id -g) \ ${BUILD_ARGS} \ - --name console-proxy-builder \ + --name stratos-jetstream-builder \ --volume $(pwd):/go/src/github.com/SUSE/stratos-ui \ - splatform/stratos-proxy-builder:opensuse $* + splatform/stratos-jetstream-builder:opensuse $* ret=$? popd diff --git a/deploy/ci/tasks/stratos-ui/prep-proxy-image.yml b/deploy/ci/tasks/stratos-ui/prep-proxy-image.yml index e0132e97b0..ca5ca3fd93 100644 --- a/deploy/ci/tasks/stratos-ui/prep-proxy-image.yml +++ b/deploy/ci/tasks/stratos-ui/prep-proxy-image.yml @@ -3,7 +3,7 @@ platform: linux image_resource: type: docker-image source: - repository: ci-registry.capbristol.com:5000/splatform/stratos-proxy-builder + repository: ci-registry.capbristol.com:5000/splatform/stratos-jetstream-builder tag: "opensuse" insecure_registries: [ "ci-registry.capbristol.com:5000" ] @@ -17,7 +17,7 @@ run: - -exc - | cd stratos-ui - npm install --production + npm install npm run build-backend cd - cp -r ./stratos-ui/outputs ./portal-proxy-output diff --git a/deploy/docker-compose/build.sh b/deploy/docker-compose/build.sh index 3a4d4d411a..851cff3419 100755 --- a/deploy/docker-compose/build.sh +++ b/deploy/docker-compose/build.sh @@ -205,9 +205,9 @@ function buildProxy { -e USER_NAME=$(id -nu) \ -e USER_ID=$(id -u) \ -e GROUP_ID=$(id -g) \ - --name stratos-proxy-builder \ + --name stratos-jetstream-builder \ --volume $(pwd):/go/src/github.com/SUSE/stratos-ui \ - ${DOCKER_REGISTRY}/${DOCKER_ORG}/stratos-proxy-builder:opensuse + ${DOCKER_REGISTRY}/${DOCKER_ORG}/stratos-jetstream-builder:opensuse popd > /dev/null 2>&1 popd > /dev/null 2>&1 diff --git a/deploy/kubernetes/build.sh b/deploy/kubernetes/build.sh index edf3d13820..dfe7b4e694 100755 --- a/deploy/kubernetes/build.sh +++ b/deploy/kubernetes/build.sh @@ -214,10 +214,10 @@ function buildProxy { # Use the existing build container to compile the proxy executable, and leave # it on the local filesystem. echo - echo "-- Building the Console Proxy" + echo "-- Building the Stratos Backend" echo - echo "-- Run the build container to build the Console backend" + echo "-- Run the build container to build the Stratos backend" pushd ${STRATOS_UI_PATH} > /dev/null 2>&1 pushd $(git rev-parse --show-toplevel) > /dev/null 2>&1 @@ -229,9 +229,9 @@ function buildProxy { -e USER_NAME=$(id -nu) \ -e USER_ID=$(id -u) \ -e GROUP_ID=$(id -g) \ - --name stratos-proxy-builder \ + --name stratos-jetstream-builder \ --volume $(pwd):/go/src/github.com/SUSE/stratos-ui \ - ${DOCKER_REGISTRY}/${DOCKER_ORG}/stratos-proxy-builder:${BASE_IMAGE_TAG} + ${DOCKER_REGISTRY}/${DOCKER_ORG}/stratos-jetstream-builder:${BASE_IMAGE_TAG} popd > /dev/null 2>&1 popd > /dev/null 2>&1 @@ -255,9 +255,9 @@ function buildPostflightJob { -e USER_ID=$(id -u) \ -e GROUP_ID=$(id -g) \ -e BUILD_DB_MIGRATOR="true" \ - --name stratos-proxy-builder \ + --name stratos-jetstream-builder \ --volume $(pwd):/go/src/github.com/SUSE/stratos-ui \ - ${DOCKER_REGISTRY}/${DOCKER_ORG}/stratos-proxy-builder:${BASE_IMAGE_TAG} + ${DOCKER_REGISTRY}/${DOCKER_ORG}/stratos-jetstream-builder:${BASE_IMAGE_TAG} buildAndPublishImage stratos-postflight-job deploy/db/Dockerfile.k8s.postflight-job ${STRATOS_UI_PATH} popd > /dev/null 2>&1 diff --git a/deploy/stratos-base-images/build-base-images.sh b/deploy/stratos-base-images/build-base-images.sh index a9d7c4ee90..beb40d4c2e 100755 --- a/deploy/stratos-base-images/build-base-images.sh +++ b/deploy/stratos-base-images/build-base-images.sh @@ -127,7 +127,7 @@ build_ui_base; build_bk_base; # Used for hosting nginx build_nginx_base; -# Used for stratos-proxy-builder base +# Used for stratos-jetstream-builder base build_bk_build_base; # Used for building the backend build_portal_proxy_builder; diff --git a/deploy/tools/build-push-proxy-builder-image.sh b/deploy/tools/build-push-proxy-builder-image.sh index f5467c9409..e2b004f7ab 100755 --- a/deploy/tools/build-push-proxy-builder-image.sh +++ b/deploy/tools/build-push-proxy-builder-image.sh @@ -46,7 +46,7 @@ else echo " REGISTRY: ${DOCKER_REGISTRY}" echo " ORG: ${DOCKER_ORG}" fi -NAME=stratos-proxy-builder +NAME=stratos-jetstream-builder TAG=${TAG:-opensuse} BK_BUILD_BASE=${BK_BUILD_BASE:-splatform/stratos-bk-build-base:opensuse} From 49d5ff4a8929be56e9112f9c0c58d0c5e4d3b0aa Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 14 Feb 2018 12:45:21 +0000 Subject: [PATCH 30/30] Rough doc for cf user management --- docs/planning/cf-user-management.md | 83 +++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 docs/planning/cf-user-management.md diff --git a/docs/planning/cf-user-management.md b/docs/planning/cf-user-management.md new file mode 100644 index 0000000000..0a7be4a14f --- /dev/null +++ b/docs/planning/cf-user-management.md @@ -0,0 +1,83 @@ +# Cloud Foundry User Management + +## Requirements + +* View the organisation and space roles for all users +* Edit the organisation and space roles for a specific user +* Assign multiple organisation and/or space roles for multiple users + +## V1 + +* Users tab + table at CF, organisation and space level + * CF level shows user _organisation_ roles for all organisations as pills + * Org level shows user _space_ roles for all spaces as pills + * Space level shows user _space_ roles for space as pills +* Table provided links to update roles + * 'Manage' a single user - pop up showing orgs/spaces (depending on level) roles. Allowed edit of all shown roles + * 'Change' multiple users - similar to manage, however no existing roles were shown. New selection _replaced_ existing roles + * 'Remove all' roles of selected users. These are specific to the level (cf - remove all orgs/spaces, org - remove all org/spaces, space - remove all space) +* All cf/org/space pages allow user to 'Assign' roles in a pop up + * pop up contains stepper, one stage to select user/s and another role/s + * can only assign roles to a single org and it's spaces + * no existing roles are shown + +### V1 Issues + +* Handling large amount of orgs or spaces + * Pill format to represent user roles lead to potentially large blobs of pills which were hard to extract information from but did show + the data in as small as possible area + * The Manage/Change popup didn't scale well at the CF level for lots of orgs or org level for lots of spaces +* Only showing org or space roles in the table can lead to confusion, for example user edits org roles at space level and no changes to table. +* Multiple ways to reach the same window (buttons above tables, row actions) +* Can only mass assign roles one org at a time +* No concept of inviting users + * Need to understand how this would work by supplying an email. API provides a way to create users with their UAA guid + +## V2 + +### First pass implementation - Changes to V1 + +* 'Change' multiple user modal + * To be removed. The ability to 'reset' multiple users needs more work. +* 'Manage' single user modal + * Only allow edit of a single org and it's spaces in the 'Manage' pop up, as per the assign. This restricts functionality but presents the + information in a clearer way and scales much better for multiple orgs. + * Tidy the position of 'remove from org' + * Stretch - Remove the 'org user' and handle automatically in the background? +* 'Assign' + * Step 2 - Ensure consistent UX with 'Manage' or vice versa +* Users Tab/Table + * Show org and space columns for their roles. Relevant to level (cf - both, org - both, space - space only) + * Provide a way to collapse list of roles automatically when there's a large amount. + * Stretch - Provide a way to filter per role. This will allow user to quickly see who's, for instance, a manager. + +### Second pass + +* Invite/Create user +* Remove user +* Reset users roles + +### Design input required pre release + +* Validate first pass approach +* Review use case/possible solutions to update/reset multiple users roles +* Review use of pills in table +* Review second roles column in table +* Review Manage/Assign layout + +## Similar Implementations + +High level description of other CF UIs + +* Management at org and space level + * Roughly table like views + * Each user listed in rows + * Org/Space roles as columns +* Editing a users roles by.. + * a check box in the roles column (each change an individual api request at time of click) + * a pop up allowing edits to all org OR space roles for a specific row/user + * All edits are very specific to user +* Provide a way to invite new users by email address. + * Can specify what roles they have at invite time + +>> Note .. There's no easy way to check user/s management at Cloud Foundry admin level, which is where some of the fun starts.