Skip to content

Commit

Permalink
Merge pull request #3443 from cloudfoundry-incubator/user-services-sp…
Browse files Browse the repository at this point in the history
…ace-list

Add a user provided service instance tab & list to the space page
  • Loading branch information
richard-cox authored Apr 4, 2019
2 parents 40da9b2 + 789c6f9 commit 63d50da
Show file tree
Hide file tree
Showing 15 changed files with 335 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy {
{ value: space.entity.name, routerLink: `${baseOrgUrl}/spaces/${space.metadata.guid}/service-instances` }
]
},
{
key: 'space-user-services',
breadcrumbs: [
...baseSpaceBreadcrumbs,
{ value: space.entity.name, routerLink: `${baseOrgUrl}/spaces/${space.metadata.guid}/user-service-instances` }
]
},
{
key: 'space-routes',
breadcrumbs: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ import {
import {
CloudFoundrySpaceSummaryComponent,
} from './tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component';
import {
CloudFoundrySpaceUserServiceInstancesComponent,
} from './tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/tabs/cloud-foundry-space-user-service-instances/cloud-foundry-space-user-service-instances.component';
import {
CloudFoundrySpaceUsersComponent,
} from './tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/tabs/cloud-foundry-space-users/cloud-foundry-space-users.component';
Expand Down Expand Up @@ -143,6 +146,7 @@ import { UsersRolesComponent } from './users/manage-users/manage-users.component
CloudFoundrySpaceBaseComponent,
CloudFoundrySpaceAppsComponent,
CloudFoundrySpaceServiceInstancesComponent,
CloudFoundrySpaceUserServiceInstancesComponent,
CloudFoundrySpaceRoutesComponent,
CloudFoundrySpaceUsersComponent,
EditSpaceStepComponent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ import {
import {
CloudFoundrySpaceSummaryComponent,
} from './tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/tabs/cloud-foundry-space-summary/cloud-foundry-space-summary.component';
import {
CloudFoundrySpaceUserServiceInstancesComponent,
} from './tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/tabs/cloud-foundry-space-user-service-instances/cloud-foundry-space-user-service-instances.component';
import {
CloudFoundrySpaceUsersComponent,
} from './tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/tabs/cloud-foundry-space-users/cloud-foundry-space-users.component';
Expand Down Expand Up @@ -297,6 +300,10 @@ const cloudFoundry: Routes = [{
path: 'service-instances',
component: CloudFoundrySpaceServiceInstancesComponent
},
{
path: 'user-service-instances',
component: CloudFoundrySpaceUserServiceInstancesComponent
},
{
path: 'routes',
component: CloudFoundrySpaceRoutesComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export class CloudFoundrySpaceBaseComponent implements OnDestroy {
link: 'service-instances',
label: 'Service Instances'
},
{
link: 'user-service-instances',
label: 'User Service Instances'
},
{
link: 'routes',
label: 'Routes',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="spaces-service-instances">
<app-list></app-list>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { DatePipe } from '@angular/common';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { BaseTestModules } from '../../../../../../../../test-framework/cloud-foundry-endpoint-service.helper';
import { getCfSpaceServiceMock } from '../../../../../../../../test-framework/cloud-foundry-space.service.mock';
import { CloudFoundrySpaceUserServiceInstancesComponent } from './cloud-foundry-space-user-service-instances.component';

describe('CloudFoundrySpaceUserServiceInstancesComponent', () => {
let component: CloudFoundrySpaceUserServiceInstancesComponent;
let fixture: ComponentFixture<CloudFoundrySpaceUserServiceInstancesComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [CloudFoundrySpaceUserServiceInstancesComponent],
imports: [...BaseTestModules],
providers: [getCfSpaceServiceMock, DatePipe]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(CloudFoundrySpaceUserServiceInstancesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Component } from '@angular/core';

import {
CfUserServiceInstancesListConfigBase,
} from '../../../../../../../shared/components/list/list-types/cf-services/cf-user-service-instances-list-config';
import { ListConfig } from '../../../../../../../shared/components/list/list.component.types';

@Component({
selector: 'app-cloud-foundry-space-user-service-instances',
templateUrl: './cloud-foundry-space-user-service-instances.component.html',
styleUrls: ['./cloud-foundry-space-user-service-instances.component.scss'],
providers: [
{
provide: ListConfig,
useClass: CfUserServiceInstancesListConfigBase
}
]
})
export class CloudFoundrySpaceUserServiceInstancesComponent { }
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const enum CreateServiceFormMode {

export const CANCEL_SPACE_ID_PARAM = 'space-guid';
export const CANCEL_ORG_ID_PARAM = 'org-guid';
export const CANCEL_USER_PROVIDED = 'up';

interface ViewDetail {
showSelectCf: boolean;
Expand Down Expand Up @@ -59,6 +60,7 @@ export class CsiModeService {
this.cancelUrl = `/services`;
const spaceGuid = activatedRoute.snapshot.queryParams[CANCEL_SPACE_ID_PARAM];
const orgGuid = activatedRoute.snapshot.queryParams[CANCEL_ORG_ID_PARAM];
const isUserProvided = activatedRoute.snapshot.queryParams[CANCEL_USER_PROVIDED];
const cfId = getIdFromRoute(activatedRoute, 'endpointId');
const id = getIdFromRoute(activatedRoute, 'id');

Expand Down Expand Up @@ -106,7 +108,9 @@ export class CsiModeService {
}

if (spaceGuid && orgGuid) {
this.cancelUrl = `/cloud-foundry/${cfId}/organizations/${orgGuid}/spaces/${spaceGuid}/service-instances`;
this.cancelUrl =
// tslint:disable-next-line:max-line-length
`/cloud-foundry/${cfId}/organizations/${orgGuid}/spaces/${spaceGuid}/${isUserProvided ? 'user-service-instances' : 'service-instances'}`;
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class CfServiceInstancesListConfigBase implements IListConfig<APIResource
protected serviceInstanceColumns: ITableColumn<APIResource<IServiceInstance>>[] = [
{
columnId: 'name',
headerCell: () => 'Service Instances',
headerCell: () => 'Service Instance',
cellDefinition: {
getValue: (row) => `${row.entity.name}`
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import { DatePipe } from '@angular/common';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';

import { ListView } from '../../../../../../../store/src/actions/list.actions';
import { AppState } from '../../../../../../../store/src/app-state';
import { APIResource } from '../../../../../../../store/src/types/api.types';
import { IUserProvidedServiceInstance } from '../../../../../core/cf-api-svc.types';
import { CurrentUserPermissions } from '../../../../../core/current-user-permissions.config';
import { CurrentUserPermissionsService } from '../../../../../core/current-user-permissions.service';
import { CloudFoundrySpaceService } from '../../../../../features/cloud-foundry/services/cloud-foundry-space.service';
import { ServiceActionHelperService } from '../../../../data-services/service-action-helper.service';
import {
CANCEL_ORG_ID_PARAM,
CANCEL_SPACE_ID_PARAM,
CANCEL_USER_PROVIDED,
} from '../../../add-service-instance/csi-mode.service';
import { ListDataSource } from '../../data-sources-controllers/list-data-source';
import { ITableColumn } from '../../list-table/table.types';
import { defaultPaginationPageSizeOptionsTable, IListAction, IListConfig, ListViewTypes } from '../../list.component.types';
import {
CfSpacesUserServiceInstancesDataSource,
} from '../cf-spaces-service-instances/cf-spaces-user-service-instances-data-source';
import {
TableCellServiceInstanceAppsAttachedComponent,
} from '../cf-spaces-service-instances/table-cell-service-instance-apps-attached/table-cell-service-instance-apps-attached.component';
import {
TableCellServiceInstanceTagsComponent,
} from '../cf-spaces-service-instances/table-cell-service-instance-tags/table-cell-service-instance-tags.component';
import {
TableCellSpaceNameComponent,
} from '../cf-spaces-service-instances/table-cell-space-name/table-cell-space-name.component';

interface CanCache {
[spaceGuid: string]: Observable<boolean>;
}

@Injectable()
export class CfUserServiceInstancesListConfigBase implements IListConfig<APIResource<IUserProvidedServiceInstance>> {
viewType = ListViewTypes.TABLE_ONLY;
pageSizeOptions = defaultPaginationPageSizeOptionsTable;
dataSource: ListDataSource<APIResource>;
defaultView = 'table' as ListView;
text = {
title: null,
filter: null,
noEntries: 'There are no user provided service instances'
};

private canDetachCache: CanCache = {};
private canDeleteCache: CanCache = {};

protected serviceInstanceColumns: ITableColumn<APIResource<IUserProvidedServiceInstance>>[] = [
{
columnId: 'name',
headerCell: () => 'Service Instance',
cellDefinition: {
getValue: (row) => `${row.entity.name}`
},
cellFlex: '2'
},
{
columnId: 'space',
headerCell: () => 'Space',
cellComponent: TableCellSpaceNameComponent,
cellFlex: '1'
},
{
columnId: 'route',
headerCell: () => 'Route Service URL',
cellDefinition: {
getValue: (row) => `${row.entity.route_service_url}`
},
cellFlex: '2'
},
{
columnId: 'syslog',
headerCell: () => 'Syslog Drain URL',
cellDefinition: {
getValue: (row) => `${row.entity.syslog_drain_url}`
},
cellFlex: '2'
},
{
columnId: 'tags',
headerCell: () => 'Tags',
cellComponent: TableCellServiceInstanceTagsComponent,
cellFlex: '2'
},
{
columnId: 'attachedApps',
headerCell: () => 'Attached Applications',
cellComponent: TableCellServiceInstanceAppsAttachedComponent,
cellFlex: '3'
},
{
columnId: 'creation', headerCell: () => 'Creation Date',
cellDefinition: {
getValue: (row: APIResource) => `${this.datePipe.transform(row.metadata.created_at, 'medium')}`
},
sort: {
type: 'sort',
orderKey: 'creation',
field: 'metadata.created_at'
},
cellFlex: '2'
},
];

private listActionDelete: IListAction<APIResource> = {
action: (item: APIResource) => this.deleteServiceInstance(item),
label: 'Delete',
description: 'Delete Service Instance',
createVisible: (row$: Observable<APIResource<IUserProvidedServiceInstance>>) =>
row$.pipe(
switchMap(
row => this.can(this.canDeleteCache, CurrentUserPermissions.SERVICE_INSTANCE_DELETE, row.entity.cfGuid, row.entity.space_guid)
)
)
};

private listActionDetach: IListAction<APIResource> = {
action: (item: APIResource) => this.deleteServiceBinding(item),
label: 'Unbind',
description: 'Unbind Service Instance',
createEnabled: (row$: Observable<APIResource<IUserProvidedServiceInstance>>) =>
row$.pipe(map(row => row.entity.service_bindings.length !== 0)),
createVisible: (row$: Observable<APIResource<IUserProvidedServiceInstance>>) =>
row$.pipe(
switchMap(
row => this.can(this.canDetachCache, CurrentUserPermissions.SERVICE_BINDING_EDIT, row.entity.cfGuid, row.entity.space_guid)
)
)
};

private listActionEdit: IListAction<APIResource> = {
action: (item: APIResource<IUserProvidedServiceInstance>) =>
this.serviceActionHelperService.editServiceBinding(item.metadata.guid, item.entity.cfGuid, {
[CANCEL_SPACE_ID_PARAM]: item.entity.space_guid,
[CANCEL_ORG_ID_PARAM]: item.entity.space.entity.organization_guid,
[CANCEL_USER_PROVIDED]: true
}, true),
label: 'Edit',
description: 'Edit Service Instance',
createVisible: (row$: Observable<APIResource<IUserProvidedServiceInstance>>) =>
row$.pipe(
switchMap(
row => this.can(this.canDetachCache, CurrentUserPermissions.SERVICE_BINDING_EDIT, row.entity.cfGuid, row.entity.space_guid)
)
)
};

private can(cache: CanCache, perm: CurrentUserPermissions, cfGuid: string, spaceGuid: string): Observable<boolean> {
let can = cache[spaceGuid];
if (!can) {
can = this.currentUserPermissionsService.can(perm, cfGuid, spaceGuid);
cache[spaceGuid] = can;
}
return can;
}

constructor(
protected store: Store<AppState>,
cfSpaceService: CloudFoundrySpaceService,
protected datePipe: DatePipe,
protected currentUserPermissionsService: CurrentUserPermissionsService,
private serviceActionHelperService: ServiceActionHelperService
) {
this.dataSource = new CfSpacesUserServiceInstancesDataSource(cfSpaceService.cfGuid, cfSpaceService.spaceGuid, this.store, this);
this.serviceInstanceColumns.find(column => column.columnId === 'attachedApps').cellConfig = {
breadcrumbs: 'space-user-services'
};
}

deleteServiceInstance = (serviceInstance: APIResource<IUserProvidedServiceInstance>) =>
this.serviceActionHelperService.deleteServiceInstance(
serviceInstance.metadata.guid,
serviceInstance.entity.name,
serviceInstance.entity.cfGuid,
true
)


deleteServiceBinding = (serviceInstance: APIResource<IUserProvidedServiceInstance>) => {
this.serviceActionHelperService.detachServiceBinding(
serviceInstance.entity.service_bindings,
serviceInstance.metadata.guid,
serviceInstance.entity.cfGuid,
false,
true
);
}

getGlobalActions = () => [];
getMultiActions = () => [];
getSingleActions = () => [this.listActionEdit, this.listActionDetach, this.listActionDelete];
getMultiFiltersConfigs = () => [];
getColumns = () => this.serviceInstanceColumns;
getDataSource = () => this.dataSource;

}
Loading

0 comments on commit 63d50da

Please sign in to comment.