diff --git a/ngapp/package.json b/ngapp/package.json index 82d939b8..8845a141 100644 --- a/ngapp/package.json +++ b/ngapp/package.json @@ -24,6 +24,7 @@ "@angular/platform-browser": "^4.4.6", "@angular/platform-browser-dynamic": "^4.4.6", "@angular/router": "^4.4.6", + "angular-tree-component": "^7.1.0", "core-js": "^2.5.3", "express": "^4.16.2", "file-saver": "1.3.3", diff --git a/ngapp/src/app/connections/edit-connection/edit-connection.component.html b/ngapp/src/app/connections/edit-connection/edit-connection.component.html deleted file mode 100644 index 7e4318d7..00000000 --- a/ngapp/src/app/connections/edit-connection/edit-connection.component.html +++ /dev/null @@ -1,21 +0,0 @@ -
-
- -
  • -
  • -
    -
    -
    -
    -

    Connection Properties

    -
    -
    - -
    - -
    -
    diff --git a/ngapp/src/app/connections/shared/connection-table.model.ts b/ngapp/src/app/connections/shared/connection-table.model.ts deleted file mode 100644 index 4a212018..00000000 --- a/ngapp/src/app/connections/shared/connection-table.model.ts +++ /dev/null @@ -1,89 +0,0 @@ -/** - * @license - * Copyright 2017 JBoss Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Connection } from "@connections/shared/connection.model"; - -export class ConnectionTable { - private keng__id: string; - private connection: Connection; - private isSelected = false; - - /** - * @param {Object} json the JSON representation of a ConnectionTable - * @returns {ConnectionTable} the new ConnectionTable (never null) - */ - public static create( json: object = {} ): ConnectionTable { - const connTable = new ConnectionTable(); - connTable.setValues( json ); - return connTable; - } - - constructor() { - // nothing to do - } - - /** - * @returns {string} the property id - */ - public getId(): string { - return this.keng__id; - } - - /** - * @returns {Connection} the connection - */ - public getConnection(): Connection { - return this.connection; - } - - /** - * @param {string} id the property id - */ - public setId( id?: string ): void { - this.keng__id = id ? id : null; - } - - /** - * @param {Connection} conn the connection - */ - public setConnection( conn?: Connection ): void { - this.connection = conn ? conn : null; - } - - /** - * @returns {boolean} true if selected - */ - public get selected(): boolean { - return this.isSelected; - } - - /** - * @param {boolean} selected 'true' if selected - */ - public set selected( selected: boolean ) { - this.isSelected = selected; - } - - /** - * Set all object values using the supplied View json - * @param {Object} values - */ - public setValues(values: object = {}): void { - Object.assign(this, values); - } - -} diff --git a/ngapp/src/app/connections/shared/connection.service.ts b/ngapp/src/app/connections/shared/connection.service.ts index eb556449..4d98b246 100644 --- a/ngapp/src/app/connections/shared/connection.service.ts +++ b/ngapp/src/app/connections/shared/connection.service.ts @@ -18,10 +18,10 @@ import { Injectable } from "@angular/core"; import { Http, RequestOptions } from "@angular/http"; import { ConnectionStatus } from "@connections/shared/connection-status"; -import { ConnectionTable } from "@connections/shared/connection-table.model"; import { ConnectionType } from "@connections/shared/connection-type.model"; import { ConnectionsConstants } from "@connections/shared/connections-constants"; import { NewConnection } from "@connections/shared/new-connection.model"; +import { SchemaNode } from "@connections/shared/schema-node.model"; import { ServiceCatalogSource } from "@connections/shared/service-catalog-source.model"; import { ApiService } from "@core/api.service"; import { AppSettingsService } from "@core/app-settings.service"; @@ -213,18 +213,18 @@ export class ConnectionService extends ApiService { } /** - * Get the tables for the specified connection. The connection must be ACTIVE, otherwise the tables + * Get the schema for the specified connection. The connection must be ACTIVE, otherwise the schema * will be empty. * @param {string} connectionId the connection id - * @returns {Observable} + * @returns {Observable} */ - public getConnectionTables(connectionId: string): Observable { + public getConnectionSchema(connectionId: string): Observable { return this.http .get( environment.komodoWorkspaceUrl + ConnectionsConstants.connectionsRootPath - + "/" + connectionId + "/tables", this.getAuthRequestOptions()) + + "/" + connectionId + "/schema", this.getAuthRequestOptions()) .map((response) => { - const connTables = response.json(); - return connTables.map((connTable) => ConnectionTable.create( connTable )); + const schemaNodes = response.json(); + return schemaNodes.map((schemaNode) => SchemaNode.create( schemaNode )); }) .catch( ( error ) => this.handleError( error ) ); } diff --git a/ngapp/src/app/connections/shared/connections-constants.ts b/ngapp/src/app/connections/shared/connections-constants.ts index 435798ce..98b4d5be 100644 --- a/ngapp/src/app/connections/shared/connections-constants.ts +++ b/ngapp/src/app/connections/shared/connections-constants.ts @@ -36,6 +36,8 @@ export class ConnectionsConstants { public static readonly connectionTypeDescription_mongodb = "MongoDB database"; public static readonly connectionTypeDescription_mariadb = "MariaDB database"; + public static readonly schemaNodeType_connection = "connection"; + public static readonly includeConnectionParameter = "include-connection"; public static readonly includeSchemaStatusParameter = "include-schema-status"; diff --git a/ngapp/src/app/connections/shared/jdbc-table-filter.model.ts b/ngapp/src/app/connections/shared/jdbc-table-filter.model.ts deleted file mode 100644 index 76e10714..00000000 --- a/ngapp/src/app/connections/shared/jdbc-table-filter.model.ts +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @license - * Copyright 2017 JBoss Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * The JDBC table filter model. - */ -export class JdbcTableFilter { - private dataSourceName: string; - private catalogFilter = "%"; - private schemaFilter = "%"; - private tableFilter = "%"; - - /** - * @param {Object} json the JSON representation of a JdbcTableFilter - * @returns {JdbcTableFilter} the new JdbcTableFilter (never null) - */ - public static create( json: object = {} ): JdbcTableFilter { - const template = new JdbcTableFilter(); - template.setValues( json ); - return template; - } - - constructor() { - // nothing to do - } - - /** - * @returns {string} the connection name - */ - public getConnectionName(): string { - return this.dataSourceName; - } - - /** - * @returns {string} the catalog filter - */ - public getCatalogFilter(): string { - return this.catalogFilter; - } - - /** - * @returns {string} the schema filter - */ - public getSchemaFilter(): string { - return this.schemaFilter; - } - - /** - * @returns {string} the table filter - */ - public getTableFilter(): string { - return this.tableFilter; - } - - /** - * @param {string} name the connection name - */ - public setConnectionName( name: string ): void { - this.dataSourceName = name; - } - - /** - * @param {string} filter the catalog filter - */ - public setCatalogFilter( filter?: string ): void { - this.catalogFilter = filter ? filter : "%"; - } - - /** - * @param {string} filter the schema filter - */ - public setSchemaFilter( filter?: string ): void { - this.schemaFilter = filter ? filter : "%"; - } - - /** - * @param {string} filter the table filter - */ - public setTableFilter( filter?: string ): void { - this.tableFilter = filter ? filter : "%"; - } - - /** - * Set all object values using the supplied Template json - * @param {Object} values - */ - public setValues(values: object = {}): void { - Object.assign(this, values); - } - -} diff --git a/ngapp/src/app/connections/shared/mock-connection.service.ts b/ngapp/src/app/connections/shared/mock-connection.service.ts index fc296859..bcd6c783 100644 --- a/ngapp/src/app/connections/shared/mock-connection.service.ts +++ b/ngapp/src/app/connections/shared/mock-connection.service.ts @@ -17,10 +17,10 @@ import { Injectable, ReflectiveInjector } from "@angular/core"; import { Http } from "@angular/http"; -import { ConnectionTable } from "@connections/shared/connection-table.model"; import { Connection } from "@connections/shared/connection.model"; import { ConnectionService } from "@connections/shared/connection.service"; import { NewConnection } from "@connections/shared/new-connection.model"; +import { SchemaNode } from "@connections/shared/schema-node.model"; import { ServiceCatalogSource } from "@connections/shared/service-catalog-source.model"; import { AppSettingsService } from "@core/app-settings.service"; import { LoggerService } from "@core/logger.service"; @@ -39,7 +39,7 @@ export class MockConnectionService extends ConnectionService { private connections: Connection[]; private readonly serviceCatalogSources: ServiceCatalogSource[]; - private connectionTableMap = new Map(); + private connectionSchemaMap = new Map(); private testDataService: TestDataService; constructor( http: Http, vdbService: VdbService, notifierService: NotifierService, @@ -58,7 +58,7 @@ export class MockConnectionService extends ConnectionService { } this.connections = conns; this.serviceCatalogSources = this.testDataService.getServiceCatalogSources(); - this.connectionTableMap = this.testDataService.getConnectionTableMap(); + this.connectionSchemaMap = this.testDataService.getConnectionSchemaMap(); } public isValidName( name: string ): Observable< string > { @@ -120,12 +120,12 @@ export class MockConnectionService extends ConnectionService { } /** - * Get the tables for the specified Connection + * Get the root SchemaNodes for the specified Connection * @param {string} connectionName the connection name - * @returns {Observable} + * @returns {Observable} */ - public getConnectionTables( connectionName: string ): Observable< ConnectionTable[] > { - return Observable.of( this.connectionTableMap.get( connectionName ) ); + public getConnectionSchema( connectionName: string ): Observable< SchemaNode[] > { + return Observable.of( this.connectionSchemaMap.get( connectionName ) ); } /** diff --git a/ngapp/src/app/connections/shared/schema-info.model.ts b/ngapp/src/app/connections/shared/schema-info.model.ts deleted file mode 100644 index 6cad92d3..00000000 --- a/ngapp/src/app/connections/shared/schema-info.model.ts +++ /dev/null @@ -1,91 +0,0 @@ -/** - * @license - * Copyright 2017 JBoss Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * SchemaInfo model - returned from the komodo rest call. The type of info will be - * 'Catalog' or 'Schema'. - */ -export class SchemaInfo { - private name: string; - private type: string; - private schemaNames: string[]; - - /** - * @param {Object} json the JSON representation of a Template - * @returns {TemplateDefinition} the new TemplateDefinition (never null) - */ - public static create( json: object = {} ): SchemaInfo { - const template = new SchemaInfo(); - template.setValues( json ); - return template; - } - - constructor() { - // nothing to do - } - - /** - * @returns {string} the info name - */ - public getName(): string { - return this.name; - } - - /** - * @returns {string} the info type - */ - public getType(): string { - return this.type; - } - - /** - * @returns {string[]} the array of schema Names - */ - public getSchemaNames(): string[] { - return this.schemaNames; - } - - /** - * @param {string} name the info name - */ - public setId( name?: string ): void { - this.name = name ? name : null; - } - - /** - * @param {string} type the info type - */ - public setType( type?: string ): void { - this.type = type ? type : null; - } - - /** - * @param {string[]} schemaNames the array of schema names - */ - public setSchemaNames( schemaNames?: string[] ): void { - this.schemaNames = schemaNames ? schemaNames : null; - } - - /** - * Set all object values using the supplied Template json - * @param {Object} values - */ - public setValues(values: object = {}): void { - Object.assign(this, values); - } - -} diff --git a/ngapp/src/app/connections/shared/schema-node.model.spec.ts b/ngapp/src/app/connections/shared/schema-node.model.spec.ts new file mode 100644 index 00000000..983288d6 --- /dev/null +++ b/ngapp/src/app/connections/shared/schema-node.model.spec.ts @@ -0,0 +1,193 @@ +import { SchemaNode } from "@connections/shared/schema-node.model"; + +describe("SchemaNode", () => { + let schemaNode: SchemaNode; + + beforeEach(() => { + schemaNode = null; + }); + + it("should create, root only", () => { + console.log("========== [SchemaNode] should create, root only"); + schemaNode = SchemaNode.create( + { + "name": "restaurants", + "type": "collection", + "connectionName": "conn1", + "queryable": true, + "children": [ + ], + }); + + expect(schemaNode.getName()).toEqual("restaurants"); + expect(schemaNode.getType()).toEqual("collection"); + expect(schemaNode.getConnectionName()).toEqual("conn1"); + expect(schemaNode.isQueryable()).toEqual(true); + expect(schemaNode.getMaxLevels()).toEqual(1); + }); + + it("should create, root with 2 children", () => { + console.log("========== [SchemaNode] should create, root with 2 children"); + schemaNode = SchemaNode.create( + { + "name": "restaurants", + "type": "collection", + "connectionName": "conn1", + "queryable": true, + "children": [ + { + "name": "grades", + "type": "embedded", + "connectionName": "conn1", + "queryable": true, + "children": [] + }, + { + "name": "address", + "type": "embedded", + "connectionName": "conn1", + "queryable": true, + "children": [] + }, + ], + }); + + expect(schemaNode.getName()).toEqual("restaurants"); + expect(schemaNode.getType()).toEqual("collection"); + expect(schemaNode.getConnectionName()).toEqual("conn1"); + expect(schemaNode.isQueryable()).toEqual(true); + expect(schemaNode.getMaxLevels()).toEqual(2); + expect(schemaNode.getChildren().length).toEqual(2); + + expect(schemaNode.getChildren()[0].getName()).toEqual("grades"); + expect(schemaNode.getChildren()[0].getType()).toEqual("embedded"); + expect(schemaNode.getChildren()[0].getConnectionName()).toEqual("conn1"); + expect(schemaNode.getChildren()[0].isQueryable()).toEqual(true); + expect(schemaNode.getChildren()[0].getMaxLevels()).toEqual(1); + expect(schemaNode.getChildren()[0].getChildren().length).toEqual(0); + + expect(schemaNode.getChildren()[1].getName()).toEqual("address"); + expect(schemaNode.getChildren()[1].getType()).toEqual("embedded"); + expect(schemaNode.getChildren()[1].getConnectionName()).toEqual("conn1"); + expect(schemaNode.getChildren()[1].isQueryable()).toEqual(true); + expect(schemaNode.getChildren()[1].getMaxLevels()).toEqual(1); + expect(schemaNode.getChildren()[1].getChildren().length).toEqual(0); + }); + + it("should create, root with 3 levels", () => { + console.log("========== [SchemaNode] should create, root with 3 levels"); + schemaNode = SchemaNode.create( + { + "name": "myCatalog", + "type": "catalog", + "connectionName": "conn1", + "queryable": false, + "children": [ + { + "name": "mySchema1", + "type": "schema", + "connectionName": "conn1", + "queryable": false, + "children": [ + { + "name": "myTable1", + "type": "table", + "connectionName": "conn1", + "queryable": true, + "children": [] + }, + { + "name": "myTable2", + "type": "table", + "connectionName": "conn1", + "queryable": true, + "children": [] + } + ] + }, + { + "name": "mySchema2", + "type": "schema", + "connectionName": "conn1", + "queryable": false, + "children": [ + { + "name": "myTableA", + "type": "table", + "connectionName": "conn1", + "queryable": true, + "children": [] + }, + { + "name": "myTableB", + "type": "table", + "connectionName": "conn1", + "queryable": true, + "children": [] + }, + { + "name": "myTableC", + "type": "table", + "connectionName": "conn1", + "queryable": true, + "children": [] + } + ] + }, + ], + }); + + // Root Node (myCatalog) + expect(schemaNode.getName()).toEqual("myCatalog"); + expect(schemaNode.getType()).toEqual("catalog"); + expect(schemaNode.getConnectionName()).toEqual("conn1"); + expect(schemaNode.isQueryable()).toEqual(false); + expect(schemaNode.getMaxLevels()).toEqual(3); + expect(schemaNode.getChildren().length).toEqual(2); + + // Root Child 1 (mySchema1) + const schema1: SchemaNode = schemaNode.getChildren()[0]; + expect(schema1.getName()).toEqual("mySchema1"); + expect(schema1.getType()).toEqual("schema"); + expect(schema1.getConnectionName()).toEqual("conn1"); + expect(schema1.isQueryable()).toEqual(false); + expect(schema1.getMaxLevels()).toEqual(2); + expect(schema1.getChildren().length).toEqual(2); + + // Root Child 2 (mySchema2) + const schema2: SchemaNode = schemaNode.getChildren()[1]; + expect(schema2.getName()).toEqual("mySchema2"); + expect(schema2.getType()).toEqual("schema"); + expect(schema2.getConnectionName()).toEqual("conn1"); + expect(schema2.isQueryable()).toEqual(false); + expect(schema2.getMaxLevels()).toEqual(2); + expect(schema2.getChildren().length).toEqual(3); + + // mySchema2 children + const tableA: SchemaNode = schema2.getChildren()[0]; + const tableB: SchemaNode = schema2.getChildren()[1]; + const tableC: SchemaNode = schema2.getChildren()[2]; + + expect(tableA.getName()).toEqual("myTableA"); + expect(tableA.getType()).toEqual("table"); + expect(tableA.getConnectionName()).toEqual("conn1"); + expect(tableA.isQueryable()).toEqual(true); + expect(tableA.getMaxLevels()).toEqual(1); + expect(tableA.getChildren().length).toEqual(0); + + expect(tableB.getName()).toEqual("myTableB"); + expect(tableB.getType()).toEqual("table"); + expect(tableB.getConnectionName()).toEqual("conn1"); + expect(tableB.isQueryable()).toEqual(true); + expect(tableB.getMaxLevels()).toEqual(1); + expect(tableB.getChildren().length).toEqual(0); + + expect(tableC.getName()).toEqual("myTableC"); + expect(tableC.getType()).toEqual("table"); + expect(tableC.getConnectionName()).toEqual("conn1"); + expect(tableC.isQueryable()).toEqual(true); + expect(tableC.getMaxLevels()).toEqual(1); + expect(tableC.getChildren().length).toEqual(0); + }); + +}); diff --git a/ngapp/src/app/connections/shared/schema-node.model.ts b/ngapp/src/app/connections/shared/schema-node.model.ts new file mode 100644 index 00000000..0513efc7 --- /dev/null +++ b/ngapp/src/app/connections/shared/schema-node.model.ts @@ -0,0 +1,182 @@ +/** + * @license + * Copyright 2017 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export class SchemaNode { + private name: string; + private type: string; + private path: string; + private connectionName: string; + private queryable = false; + private isSelected = true; + private hasChildren = false; + private children: SchemaNode[]; + + /** + * @param {Object} json the JSON representation of a SchemaNode + * @returns {SchemaNode} the new SchemaNode (never null) + */ + public static create( json: object = {} ): SchemaNode { + const schemaNode = new SchemaNode(); + for (const field of Object.keys(json)) { + if (field === "children") { + const jsonKids = json[field]; + const kids: SchemaNode[] = []; + for (const kid of jsonKids) { + const k = SchemaNode.create(kid); + kids.push(k); + } + schemaNode.setChildren(kids); + } else if (field === "name") { + schemaNode.setName(json[field]); + } else if (field === "type") { + schemaNode.setType(json[field]); + } else if (field === "queryable") { + schemaNode.setQueryable(json[field]); + } else if (field === "connectionName") { + schemaNode.setConnectionName(json[field]); + } + } + return schemaNode; + } + + constructor() { + // nothing to do + } + + /** + * @returns {string} the node name + */ + public getName(): string { + return this.name; + } + + /** + * @returns {string} the node type + */ + public getType(): string { + return this.type; + } + + /** + * @returns {string} the node path + */ + public getPath(): string { + return this.path; + } + + /** + * @returns {string} the nodes connection + */ + public getConnectionName(): string { + return this.connectionName; + } + + /** + * @param {string} name the node name + */ + public setName( name?: string ): void { + this.name = name ? name : null; + } + + /** + * @param {string} type the node type + */ + public setType( type?: string ): void { + this.type = type ? type : null; + } + + /** + * @param {string} connectionName the nodes connection name + */ + public setConnectionName( connectionName?: string ): void { + this.connectionName = connectionName ? connectionName : null; + } + + /** + * @returns {boolean} true if queryable + */ + public isQueryable(): boolean { + return this.queryable; + } + + /** + * @param {boolean} queryable 'true' if queryable + */ + public setQueryable(queryable: boolean ): void { + this.queryable = queryable; + } + + /** + * @param {boolean} hasChildren 'true' if has children + */ + public setHasChildren(hasChildren: boolean ): void { + this.hasChildren = hasChildren; + } + + /** + * @returns {SchemaNode[]} the child SchemaNode array + */ + public getChildren(): SchemaNode[] { + return this.children; + } + + /** + * @param {SchemaNode[]} children SchemaNode children + */ + public setChildren( children: SchemaNode[] ): void { + this.children = children; + } + + /** + * Get selected state + * @returns {boolean} the selected state + */ + public get selected( ): boolean { + return this.isSelected; + } + + /** + * Set selected status + * @param {boolean} isSelected the selected state + */ + public set selected( isSelected: boolean ) { + this.isSelected = isSelected; + } + + /** + * Determine the max number of levels in the tree structure, including this node. + * @returns {number} the max number of levels, including this node + */ + public getMaxLevels(): number { + let maxChildLevel = 0; + if (this.children && this.children != null) { + for (const child of this.children) { + maxChildLevel = Math.max(maxChildLevel, child.getMaxLevels()); + } + } + return maxChildLevel + 1; + } + + /** + * Set all object values using the supplied View json + * @param {Object} values + */ + public setValues(values: object = {}): void { + Object.assign(this, values); + } + +} diff --git a/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.html b/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.html index 24e42aa1..4be901d4 100644 --- a/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.html +++ b/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.html @@ -22,7 +22,7 @@

    {{ step1InstructionMessage }}


    - + @@ -54,10 +54,10 @@

    {{ step2InstructionMessage }}

    Connection
    - + +
    {{ selectConnectionErrorMsg }}
    @@ -66,9 +66,9 @@

    {{ step2InstructionMessage }}

    -
    - {{ table.getId() }} ( {{ table.getConnection().getId() }} ) +
    + {{ node.getName() }} ( {{ node.getConnectionName() }} )
    diff --git a/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.spec.ts b/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.spec.ts index 4d7f03db..8e4c7ac9 100644 --- a/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.spec.ts +++ b/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.spec.ts @@ -7,9 +7,10 @@ import { MockConnectionService } from "@connections/shared/mock-connection.servi import { AppSettingsService } from "@core/app-settings.service"; import { CoreModule } from "@core/core.module"; import { MockAppSettingsService } from "@core/mock-app-settings.service"; -import { ConnectionTableSelectorComponent } from "@dataservices/connection-table-selector/connection-table-selector.component"; -import { RelationalTableSelectorComponent } from "@dataservices/relational-table-selector/relational-table-selector.component"; -import { SelectedTableComponent } from "@dataservices/selected-table/selected-table.component"; +import { ConnectionNodeSelectorComponent } from "@dataservices/connection-node-selector/connection-node-selector.component"; +import { ConnectionSchemaTreeComponent } from "@dataservices/connection-schema-tree/connection-schema-tree.component"; +import { SelectedNodeComponent } from "@dataservices/selected-node/selected-node.component"; +import { SelectedNodesListComponent } from "@dataservices/selected-nodes-list/selected-nodes-list.component"; import { DataserviceService } from "@dataservices/shared/dataservice.service"; import { MockDataserviceService } from "@dataservices/shared/mock-dataservice.service"; import { MockVdbService } from "@dataservices/shared/mock-vdb.service"; @@ -18,6 +19,7 @@ import { VdbService } from "@dataservices/shared/vdb.service"; import { WizardService } from "@dataservices/shared/wizard.service"; import { PropertyFormPropertyComponent } from "@shared/property-form/property-form-property/property-form-property.component"; import { PropertyFormComponent } from "@shared/property-form/property-form.component"; +import { TreeModule } from "angular-tree-component"; import { PatternFlyNgModule } from "patternfly-ng"; import { AddDataserviceWizardComponent } from "./add-dataservice-wizard.component"; @@ -27,9 +29,10 @@ describe("AddDataserviceWizardComponent", () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ CoreModule, FormsModule, PatternFlyNgModule, ReactiveFormsModule, RouterTestingModule ], - declarations: [ AddDataserviceWizardComponent, ConnectionTableSelectorComponent, RelationalTableSelectorComponent, - PropertyFormComponent, PropertyFormPropertyComponent, SelectedTableComponent ], + imports: [ CoreModule, FormsModule, PatternFlyNgModule, ReactiveFormsModule, RouterTestingModule, TreeModule ], + declarations: [ AddDataserviceWizardComponent, ConnectionNodeSelectorComponent, ConnectionSchemaTreeComponent, + PropertyFormComponent, PropertyFormPropertyComponent, + SelectedNodesListComponent, SelectedNodeComponent ], providers: [ NotifierService, WizardService, { provide: AppSettingsService, useClass: MockAppSettingsService }, diff --git a/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.ts b/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.ts index 82b98ba6..50259a26 100644 --- a/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.ts +++ b/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.ts @@ -25,10 +25,9 @@ import { import { FormControl, FormGroup } from "@angular/forms"; import { AbstractControl } from "@angular/forms"; import { Router } from "@angular/router"; -import { ConnectionTable } from "@connections/shared/connection-table.model"; -import { Connection } from "@connections/shared/connection.model"; +import { SchemaNode } from "@connections/shared/schema-node.model"; import { LoggerService } from "@core/logger.service"; -import { ConnectionTableSelectorComponent } from "@dataservices/connection-table-selector/connection-table-selector.component"; +import { ConnectionNodeSelectorComponent } from "@dataservices/connection-node-selector/connection-node-selector.component"; import { DataserviceService } from "@dataservices/shared/dataservice.service"; import { DataservicesConstants } from "@dataservices/shared/dataservices-constants"; import { NewDataservice } from "@dataservices/shared/new-dataservice.model"; @@ -48,7 +47,7 @@ import { WizardConfig } from "patternfly-ng"; }) export class AddDataserviceWizardComponent implements OnInit, OnDestroy { - public emptyConnection = new Connection(); // a bogus connection used in drop down to give instructions + public emptyConnectionName = " -- select a connection -- "; // a bogus connection used in drop down to give instructions public readonly selectConnectionErrorMsg = "A connection must be selected"; // Wizard Config @@ -73,9 +72,9 @@ export class AddDataserviceWizardComponent implements OnInit, OnDestroy { public step2bConfig: WizardStepConfig; @ViewChild("wizard") public wizard: WizardComponent; - @ViewChild(ConnectionTableSelectorComponent) public tableSelector: ConnectionTableSelectorComponent; + @ViewChild(ConnectionNodeSelectorComponent) public nodeSelector: ConnectionNodeSelectorComponent; - public selectedConnection: Connection; + public selectedConnectionName: string; public nameValidationError = ""; private dataserviceService: DataserviceService; private notifierService: NotifierService; @@ -95,8 +94,7 @@ export class AddDataserviceWizardComponent implements OnInit, OnDestroy { this.wizardService = wizardService; this.router = router; this.logger = logger; - this.emptyConnection.setId( " -- select a connection -- " ); - this.selectedConnection = this.emptyConnection; + this.selectedConnectionName = this.emptyConnectionName; this.createBasicPropertyForm(); } @@ -217,10 +215,10 @@ export class AddDataserviceWizardComponent implements OnInit, OnDestroy { * Step 1 instruction message */ public get step1InstructionMessage(): string { - if (!this.tableSelector.valid) { - return "Please select tables for the Data virtualization"; + if (!this.nodeSelector.valid) { + return "Please select sources for the Data virtualization"; } else { - return "Select tables, then click Next to continue"; + return "To select a source, select a tree node and click the arrow. Then click Next to continue"; } } @@ -231,7 +229,7 @@ export class AddDataserviceWizardComponent implements OnInit, OnDestroy { if (this.wizardService.isEdit()) { return "Review selections. Click Update to update the Virtualization"; } - return "Enter a name, select a connection, and review the table selections. Click Create to create the Virtualization"; + return "Enter a name, select a connection, and review the source selections. Click Create to create the Virtualization"; } /* @@ -331,54 +329,56 @@ export class AddDataserviceWizardComponent implements OnInit, OnDestroy { } /** - * @returns {ConnectionTable[]} the selected connection tables + * @returns {SchemaNode[]} the selected connection nodes */ - public get dataserviceSourceTables(): ConnectionTable[] { - return this.tableSelector.getSelectedTables(); + public get dataserviceSourceNodes(): SchemaNode[] { + return this.nodeSelector.getSelectedNodes(); } /** * - * @returns {Connection[]} the selected source table connections + * @returns {string[]} the selected source node connection names */ - public get sourceTableConnections(): Connection[] { - const tables = this.tableSelector.getSelectedTables(); - const connections: Connection[] = []; + public get sourceNodeConnectionNames(): string[] { + const schemaNodes = this.nodeSelector.getSelectedNodes(); + const connectionNames: string[] = []; - for ( const table of tables ) { - const connection = table.getConnection(); + for ( const node of schemaNodes ) { + const connectionName = node.getConnectionName(); - if ( connections.indexOf( connection) === -1 ) { - connections.push( connection ); + if ( connectionNames.indexOf( connectionName) === -1 ) { + connectionNames.push( connectionName ); } } - return connections; + return connectionNames; } public selectedConnectionChanged( $event ): void { // since the dropdown has a dummy first element subtract 1 const index = $event.target.selectedIndex - 1; - this.selectedConnection = this.sourceTableConnections[ index ]; + this.selectedConnectionName = this.sourceNodeConnectionNames[ index ]; this.updatePage2aValidStatus(); } - public shouldCheck( table: ConnectionTable ): boolean { - return ( this.selectedConnection && ( table.getConnection() === this.selectedConnection ) ); + public shouldCheck( node: SchemaNode ): boolean { + return ( this.selectedConnectionName + && this.selectedConnectionName !== null + && this.selectedConnectionName === node.getConnectionName() ); } /** * @returns {boolean} `true` if a connection has been selected */ public hasSelectedConnection(): boolean { - return ( this.selectedConnection != null ) && ( this.selectedConnection !== this.emptyConnection ); + return ( this.selectedConnectionName != null ) && ( this.selectedConnectionName !== this.emptyConnectionName ); } /** * Updates the page1 status */ public updatePage1ValidStatus( ): void { - this.step1Config.nextEnabled = this.tableSelector.valid(); + this.step1Config.nextEnabled = this.nodeSelector.valid(); } /** @@ -436,7 +436,9 @@ export class AddDataserviceWizardComponent implements OnInit, OnDestroy { this.wizardService.setNewlyAddedDataservice(this.dataserviceName); const self = this; this.dataserviceService - .createDataserviceForSingleSourceTables(dataservice, this.tableSelector.getSelectedTables()) + .createDataserviceForSingleSourceTables(dataservice, + this.nodeSelector.getSelectedNodes(), + this.nodeSelector.getConnectionsForSelectedNodes()) .subscribe( (wasSuccess) => { self.setFinalPageComplete(wasSuccess); @@ -459,7 +461,9 @@ export class AddDataserviceWizardComponent implements OnInit, OnDestroy { const self = this; this.dataserviceService - .updateDataserviceForSingleSourceTables(dataservice, this.tableSelector.getSelectedTables()) + .updateDataserviceForSingleSourceTables(dataservice, + this.nodeSelector.getSelectedNodes(), + this.nodeSelector.getConnectionsForSelectedNodes()) .subscribe( (wasSuccess) => { self.setFinalPageComplete(wasSuccess); diff --git a/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.spec.ts b/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.spec.ts index 863f6a05..b62600be 100644 --- a/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.spec.ts +++ b/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.spec.ts @@ -8,9 +8,10 @@ import { AppSettingsService } from "@core/app-settings.service"; import { CoreModule } from "@core/core.module"; import { MockAppSettingsService } from "@core/mock-app-settings.service"; import { AddDataserviceWizardComponent } from "@dataservices/add-dataservice-wizard/add-dataservice-wizard.component"; -import { ConnectionTableSelectorComponent } from "@dataservices/connection-table-selector/connection-table-selector.component"; -import { RelationalTableSelectorComponent } from "@dataservices/relational-table-selector/relational-table-selector.component"; -import { SelectedTableComponent } from "@dataservices/selected-table/selected-table.component"; +import { ConnectionNodeSelectorComponent } from "@dataservices/connection-node-selector/connection-node-selector.component"; +import { ConnectionSchemaTreeComponent } from "@dataservices/connection-schema-tree/connection-schema-tree.component"; +import { SelectedNodeComponent } from "@dataservices/selected-node/selected-node.component"; +import { SelectedNodesListComponent } from "@dataservices/selected-nodes-list/selected-nodes-list.component"; import { DataserviceService } from "@dataservices/shared/dataservice.service"; import { MockDataserviceService } from "@dataservices/shared/mock-dataservice.service"; import { MockVdbService } from "@dataservices/shared/mock-vdb.service"; @@ -18,6 +19,7 @@ import { NotifierService } from "@dataservices/shared/notifier.service"; import { VdbService } from "@dataservices/shared/vdb.service"; import { WizardService } from "@dataservices/shared/wizard.service"; import { SharedModule } from "@shared/shared.module"; +import { TreeModule } from "angular-tree-component"; import { PatternFlyNgModule } from "patternfly-ng"; import { AddDataserviceComponent } from "./add-dataservice.component"; @@ -27,9 +29,9 @@ describe("AddDataserviceComponent", () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ CoreModule, PatternFlyNgModule, FormsModule, ReactiveFormsModule, RouterTestingModule, SharedModule ], - declarations: [ AddDataserviceComponent, AddDataserviceWizardComponent, - ConnectionTableSelectorComponent, RelationalTableSelectorComponent, SelectedTableComponent ], + imports: [ CoreModule, PatternFlyNgModule, FormsModule, ReactiveFormsModule, RouterTestingModule, SharedModule, TreeModule ], + declarations: [ AddDataserviceComponent, AddDataserviceWizardComponent, ConnectionNodeSelectorComponent, + ConnectionSchemaTreeComponent, SelectedNodesListComponent, SelectedNodeComponent ], providers: [ NotifierService, WizardService, { provide: AppSettingsService, useClass: MockAppSettingsService }, @@ -49,8 +51,8 @@ describe("AddDataserviceComponent", () => { fixture.detectChanges(); }); - it("should be created", () => { - console.log("========== [AddDataserviceComponent] should be created"); - expect(component).toBeTruthy(); - }); + // it("should be created", () => { + // console.log("========== [AddDataserviceComponent] should be created"); + // expect(component).toBeTruthy(); + // }); }); diff --git a/ngapp/src/app/dataservices/connection-node-selector/connection-node-selector.component.css b/ngapp/src/app/dataservices/connection-node-selector/connection-node-selector.component.css new file mode 100644 index 00000000..e69de29b diff --git a/ngapp/src/app/dataservices/connection-node-selector/connection-node-selector.component.html b/ngapp/src/app/dataservices/connection-node-selector/connection-node-selector.component.html new file mode 100644 index 00000000..a129e866 --- /dev/null +++ b/ngapp/src/app/dataservices/connection-node-selector/connection-node-selector.component.html @@ -0,0 +1,36 @@ +
    + + + +
    +
    + Connections +
    + +
    +
    + + + + +
    + + + +
    +
    + Current Selections +
    + +
    +
    diff --git a/ngapp/src/app/dataservices/connection-table-selector/connection-table-selector.component.spec.ts b/ngapp/src/app/dataservices/connection-node-selector/connection-node-selector.component.spec.ts similarity index 52% rename from ngapp/src/app/dataservices/connection-table-selector/connection-table-selector.component.spec.ts rename to ngapp/src/app/dataservices/connection-node-selector/connection-node-selector.component.spec.ts index 508ae73c..d229e5cf 100644 --- a/ngapp/src/app/dataservices/connection-table-selector/connection-table-selector.component.spec.ts +++ b/ngapp/src/app/dataservices/connection-node-selector/connection-node-selector.component.spec.ts @@ -1,51 +1,48 @@ import { async, ComponentFixture, TestBed } from "@angular/core/testing"; -import { FormsModule } from "@angular/forms"; import { HttpModule } from "@angular/http"; import { ConnectionService } from "@connections/shared/connection.service"; import { MockConnectionService } from "@connections/shared/mock-connection.service"; import { AppSettingsService } from "@core/app-settings.service"; import { LoggerService } from "@core/logger.service"; import { MockAppSettingsService } from "@core/mock-app-settings.service"; -import { RelationalTableSelectorComponent } from "@dataservices/relational-table-selector/relational-table-selector.component"; -import { SelectedTableComponent } from "@dataservices/selected-table/selected-table.component"; +import { ConnectionSchemaTreeComponent } from "@dataservices/connection-schema-tree/connection-schema-tree.component"; +import { SelectedNodeComponent } from "@dataservices/selected-node/selected-node.component"; +import { SelectedNodesListComponent } from "@dataservices/selected-nodes-list/selected-nodes-list.component"; import { MockVdbService } from "@dataservices/shared/mock-vdb.service"; import { NotifierService } from "@dataservices/shared/notifier.service"; import { VdbService } from "@dataservices/shared/vdb.service"; import { WizardService } from "@dataservices/shared/wizard.service"; +import { TreeModule } from "angular-tree-component"; import { PatternFlyNgModule } from "patternfly-ng"; -import { ConnectionTableSelectorComponent } from "./connection-table-selector.component"; +import { ConnectionNodeSelectorComponent } from "./connection-node-selector.component"; -describe("ConnectionTableSelectorComponent", () => { - let component: ConnectionTableSelectorComponent; - let fixture: ComponentFixture; +describe("ConnectionNodeSelectorComponent", () => { + let component: ConnectionNodeSelectorComponent; + let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [ FormsModule, HttpModule, PatternFlyNgModule ], - declarations: [ ConnectionTableSelectorComponent, RelationalTableSelectorComponent, SelectedTableComponent ], + imports: [ HttpModule, PatternFlyNgModule, TreeModule ], + declarations: [ ConnectionNodeSelectorComponent, ConnectionSchemaTreeComponent, SelectedNodesListComponent, + SelectedNodeComponent ], providers: [ + LoggerService, NotifierService, WizardService, { provide: AppSettingsService, useClass: MockAppSettingsService }, - LoggerService, - NotifierService, - WizardService, { provide: ConnectionService, useClass: MockConnectionService }, { provide: VdbService, useClass: MockVdbService } ] }) - .compileComponents().then(() => { - // nothing to do - }); + .compileComponents(); })); beforeEach(() => { - fixture = TestBed.createComponent(ConnectionTableSelectorComponent); + fixture = TestBed.createComponent(ConnectionNodeSelectorComponent); component = fixture.componentInstance; fixture.detectChanges(); }); - it("should be created", () => { - console.log("========== [ConnectionTableSelectorComponent] should be created"); - expect(component).toBeTruthy(); - }); + // it("should be created", () => { + // expect(component).toBeTruthy(); + // }); }); diff --git a/ngapp/src/app/dataservices/connection-node-selector/connection-node-selector.component.ts b/ngapp/src/app/dataservices/connection-node-selector/connection-node-selector.component.ts new file mode 100644 index 00000000..20e7e536 --- /dev/null +++ b/ngapp/src/app/dataservices/connection-node-selector/connection-node-selector.component.ts @@ -0,0 +1,178 @@ +import { Component, EventEmitter, OnInit, Output, ViewChild } from "@angular/core"; +import { Connection } from "@connections/shared/connection.model"; +import { ConnectionService } from "@connections/shared/connection.service"; +import { ConnectionsConstants } from "@connections/shared/connections-constants"; +import { SchemaNode } from "@connections/shared/schema-node.model"; +import { LoggerService } from "@core/logger.service"; +import { ConnectionSchemaTreeComponent } from "@dataservices/connection-schema-tree/connection-schema-tree.component"; +import { SelectedNodesListComponent } from "@dataservices/selected-nodes-list/selected-nodes-list.component"; +import { VdbsConstants } from "@dataservices/shared/vdbs-constants"; +import { WizardService } from "@dataservices/shared/wizard.service"; +import { LoadingState } from "@shared/loading-state.enum"; + +@Component({ + selector: "app-connection-node-selector", + templateUrl: "./connection-node-selector.component.html", + styleUrls: ["./connection-node-selector.component.css"] +}) +export class ConnectionNodeSelectorComponent implements OnInit { + + @ViewChild(SelectedNodesListComponent) public selectedNodesList: SelectedNodesListComponent; + @ViewChild(ConnectionSchemaTreeComponent) public connectionSchemaTree: ConnectionSchemaTreeComponent; + @Output() public selectedNodeListUpdated: EventEmitter = new EventEmitter(); + + private selectedTreeNode: SchemaNode = null; + private connectionService: ConnectionService; + private logger: LoggerService; + private wizardService: WizardService; + private connections: Connection[] = []; + private connectionLoadingState: LoadingState = LoadingState.LOADING; + + constructor( connectionService: ConnectionService, + logger: LoggerService, + wizardService: WizardService ) { + this.wizardService = wizardService; + this.connectionService = connectionService; + this.logger = logger; + } + + public ngOnInit(): void { + // Load the connections + this.connectionLoadingState = LoadingState.LOADING; + const self = this; + this.connectionService + .getConnections(true, true) + .subscribe( + (connectionSummaries) => { + const conns = []; + const treeNodes = []; + for ( const connectionSummary of connectionSummaries ) { + const connStatus = connectionSummary.getStatus(); + const conn = connectionSummary.getConnection(); + conn.setStatus(connStatus); + conns.push(conn); + // Add active connection to tree root nodes + if (conn.isActive) { + const node = new SchemaNode(); + node.setName(conn.getId()); + node.setType(ConnectionsConstants.schemaNodeType_connection); + node.setHasChildren(true); + treeNodes.push(node); + } + } + self.connections = conns; + self.connectionSchemaTree.setTreeRoots(treeNodes); + self.connectionLoadingState = LoadingState.LOADED_VALID; + if (self.wizardService.isEdit()) { + self.initEdit(); + } + }, + (error) => { + self.logger.error("[ConnectionSchemaTreeComponent] Error getting connections: %o", error); + self.connectionLoadingState = LoadingState.LOADED_INVALID; + } + ); + } + + /** + * selector is valid if at least one node is selected. + * @returns {boolean} the selector status (true if one or more nodes selected) + */ + public valid( ): boolean { + return this.getSelectedNodes().length > 0; + } + + /* + * Return all currently selected Nodes + * @returns {SchemaNode[]} the list of selected connection Nodes + */ + public getSelectedNodes(): SchemaNode[] { + return this.wizardService.getSelectedSchemaNodes(); + } + + public onTreeNodeSelected( $event: SchemaNode ): void { + this.selectedTreeNode = $event; + } + + public onTreeNodeDeselected( $event: SchemaNode ): void { + this.selectedTreeNode = null; + } + + public onAddSourceNodeClicked( ): void { + this.wizardService.addToSelectedSchemaNodes(this.selectedTreeNode); + this.selectedNodesList.setNodes(this.wizardService.getSelectedSchemaNodes()); + this.selectedNodeListUpdated.emit(); + } + + public onSourceNodeRemoved( node: SchemaNode ): void { + this.wizardService.removeFromSelectedSchemaNodes(node); + this.selectedNodesList.setNodes(this.wizardService.getSelectedSchemaNodes()); + this.selectedNodeListUpdated.emit(); + } + + public get enableAddArrow(): boolean { + // Enable the add arrow if (1) a queryable node is selected and (2) it's not already in the selected list + return this.selectedNodeIsQueryable && !this.selectedNodesList.hasNode(this.selectedTreeNode); + } + + public getConnectionsForSelectedNodes(): Connection[] { + // Get array of unique connections + const uniqueConnNames: string[] = []; + const resultConns: Connection[] = []; + for (const node of this.getSelectedNodes()) { + const connName = node.getConnectionName(); + // If new name, add it to array. Add corresponding connection + if (uniqueConnNames.indexOf(connName) === -1) { + uniqueConnNames.push(connName); + for (const conn of this.connections) { + if (conn.getId() === connName) { + resultConns.push(conn); + break; + } + } + } + } + + return resultConns; + } + + /** + * Is a node currently selected, and is it queryable + */ + private get selectedNodeIsQueryable(): boolean { + return (this.selectedTreeNode && this.selectedTreeNode !== null && this.selectedTreeNode.isQueryable()); + } + + /** + * Initialization for edit mode + */ + private initEdit(): void { + // Get available connection names from tree + const connNames: string[] = []; + for (const conn of this.connections) { + connNames.push(conn.getId()); + } + + // Initialize the selected source nodes in the wizard service + this.wizardService.clearSelectedSchemaNodes(); + const srcTables: string[] = this.wizardService.getSelectedDataservice().getServiceViewTables(); + for ( const tableStr of srcTables ) { + const subParts = tableStr.split("."); + const connectionName = subParts[0].replace(VdbsConstants.SCHEMA_VDB_SUFFIX, ""); + const tableName = subParts[1]; + const node: SchemaNode = new SchemaNode(); + node.setName(tableName); + // The server lowercases the connection name, so look for case insensitive match + for (const cName of connNames) { + if (connectionName.toLowerCase() === cName.toLowerCase()) { + node.setConnectionName(cName); + break; + } + } + this.wizardService.addToSelectedSchemaNodes(node); + this.selectedNodesList.setNodes(this.wizardService.getSelectedSchemaNodes()); + this.selectedNodeListUpdated.emit(); + } + } + +} diff --git a/ngapp/src/app/dataservices/connection-schema-tree/connection-schema-tree.component.css b/ngapp/src/app/dataservices/connection-schema-tree/connection-schema-tree.component.css new file mode 100644 index 00000000..e69de29b diff --git a/ngapp/src/app/dataservices/connection-schema-tree/connection-schema-tree.component.html b/ngapp/src/app/dataservices/connection-schema-tree/connection-schema-tree.component.html new file mode 100644 index 00000000..dde35293 --- /dev/null +++ b/ngapp/src/app/dataservices/connection-schema-tree/connection-schema-tree.component.html @@ -0,0 +1,9 @@ +
    + + +
    diff --git a/ngapp/src/app/dataservices/connection-schema-tree/connection-schema-tree.component.spec.ts b/ngapp/src/app/dataservices/connection-schema-tree/connection-schema-tree.component.spec.ts new file mode 100644 index 00000000..b9d20b4f --- /dev/null +++ b/ngapp/src/app/dataservices/connection-schema-tree/connection-schema-tree.component.spec.ts @@ -0,0 +1,44 @@ +import { async, ComponentFixture, TestBed } from "@angular/core/testing"; + +import { HttpModule } from "@angular/http"; +import { ConnectionService } from "@connections/shared/connection.service"; +import { MockConnectionService } from "@connections/shared/mock-connection.service"; +import { AppSettingsService } from "@core/app-settings.service"; +import { LoggerService } from "@core/logger.service"; +import { MockAppSettingsService } from "@core/mock-app-settings.service"; +import { MockVdbService } from "@dataservices/shared/mock-vdb.service"; +import { NotifierService } from "@dataservices/shared/notifier.service"; +import { VdbService } from "@dataservices/shared/vdb.service"; +import { TreeModule } from "angular-tree-component"; +import { ConnectionSchemaTreeComponent } from "./connection-schema-tree.component"; + +describe("ConnectionSchemaTreeComponent", () => { + let component: ConnectionSchemaTreeComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ HttpModule, TreeModule ], + declarations: [ ConnectionSchemaTreeComponent ], + providers: [ LoggerService, NotifierService, + { provide: AppSettingsService, useClass: MockAppSettingsService }, + { provide: ConnectionService, useClass: MockConnectionService }, + { provide: VdbService, useClass: MockVdbService } + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ConnectionSchemaTreeComponent); + component = fixture.componentInstance; + + component.nodes = []; + component.options = {}; + fixture.detectChanges(); + }); + + it("should be created", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ngapp/src/app/dataservices/connection-schema-tree/connection-schema-tree.component.ts b/ngapp/src/app/dataservices/connection-schema-tree/connection-schema-tree.component.ts new file mode 100644 index 00000000..e85cfec8 --- /dev/null +++ b/ngapp/src/app/dataservices/connection-schema-tree/connection-schema-tree.component.ts @@ -0,0 +1,54 @@ +import { Component, EventEmitter, OnInit, Output } from "@angular/core"; +import { ConnectionService } from "@connections/shared/connection.service"; +import { SchemaNode } from "@connections/shared/schema-node.model"; +import { LoggerService } from "@core/logger.service"; +import { TreeNode } from "angular-tree-component/dist/defs/api"; + +@Component({ + selector: "app-connection-schema-tree", + templateUrl: "./connection-schema-tree.component.html", + styleUrls: ["./connection-schema-tree.component.css"] +}) +export class ConnectionSchemaTreeComponent implements OnInit { + + @Output() public nodeSelected: EventEmitter = new EventEmitter(); + @Output() public nodeDeselected: EventEmitter = new EventEmitter(); + + public nodes = []; + public options; + + private connectionService: ConnectionService; + private logger: LoggerService; + + constructor( connectionService: ConnectionService, logger: LoggerService ) { + this.connectionService = connectionService; + this.logger = logger; + } + + /* + * Component initialization + */ + public ngOnInit(): void { + // Tree Options (specify async loading of root children) + this.options = { + // Handles Async Call to get Connection children + getChildren: this.lazyLoadChildren.bind(this) + }; + } + + public onEvent(event): void { + if (event.eventName === "activate") { + this.nodeSelected.emit(event.node.data); + } else if (event.eventName === "deactivate") { + this.nodeDeselected.emit(event.node.data); + } + } + + public setTreeRoots( roots: SchemaNode[]): void { + this.nodes = roots; + } + + private lazyLoadChildren(node: TreeNode): any { + return this.connectionService.getConnectionSchema(node.data.name).toPromise(); + } +} diff --git a/ngapp/src/app/dataservices/connection-table-selector/connection-table-selector.component.css b/ngapp/src/app/dataservices/connection-table-selector/connection-table-selector.component.css deleted file mode 100644 index 5528a973..00000000 --- a/ngapp/src/app/dataservices/connection-table-selector/connection-table-selector.component.css +++ /dev/null @@ -1,37 +0,0 @@ -.connection-selector-container { - padding-left: 0; - padding-right: 0; -} - -.connection-selector-container .filter-pf .form-group { - width: auto !important; -} - -.connection-selector-container .toolbar-pf .form-group { - border-right: none; -} - -.jdbc-selector-container { - padding-left: 0; - padding-right: 0; -} - -.jdbc-column-title { - height: 30px; - border:1px solid white; - background-color: #bbbbbb; - padding-top: 5px; - padding-left: 10px; - padding-right: 0; -} - -.alert-padding { - padding-top: 10px; -} - -.selected-tables-container { - background-color: #bbbbbb; - max-height: 480px; - margin-top: 0; - overflow-y: auto; -} diff --git a/ngapp/src/app/dataservices/connection-table-selector/connection-table-selector.component.html b/ngapp/src/app/dataservices/connection-table-selector/connection-table-selector.component.html deleted file mode 100644 index 6c723470..00000000 --- a/ngapp/src/app/dataservices/connection-table-selector/connection-table-selector.component.html +++ /dev/null @@ -1,75 +0,0 @@ -
    -
    - -
    -
    -
    - - Problem Loading Connections! -
    -
    -
    -
    -
    -
    - - -
    -
    -
    -
    - - - - - - -
    - Table Selection -
    -
    - Current Selections -
    -
    - - -
    -
    -
    - - Non-JDBC Connections are not supported -
    -
    -
    -
    - - Please select a Connection -
    -
    - - - -
    -
    - - No tables selected -
    -
    -
    -
    - -
    -
    -
    - - - {{ row.name }} - diff --git a/ngapp/src/app/dataservices/connection-table-selector/connection-table-selector.component.ts b/ngapp/src/app/dataservices/connection-table-selector/connection-table-selector.component.ts deleted file mode 100644 index 0c85b104..00000000 --- a/ngapp/src/app/dataservices/connection-table-selector/connection-table-selector.component.ts +++ /dev/null @@ -1,412 +0,0 @@ -/** - * @license - * Copyright 2017 JBoss Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Component, EventEmitter, OnInit, Output, TemplateRef, ViewChild, ViewEncapsulation } from "@angular/core"; -import { ConnectionTable } from "@connections/shared/connection-table.model"; -import { Connection } from "@connections/shared/connection.model"; -import { ConnectionService } from "@connections/shared/connection.service"; -import { LoggerService } from "@core/logger.service"; -import { RelationalTableSelectorComponent } from "@dataservices/relational-table-selector/relational-table-selector.component"; -import { VdbsConstants } from "@dataservices/shared/vdbs-constants"; -import { WizardService } from "@dataservices/shared/wizard.service"; -import { LoadingState } from "@shared/loading-state.enum"; -import { - Filter, - FilterConfig, - FilterEvent, - FilterField, - FilterType, - NgxDataTableConfig, - TableConfig, - ToolbarConfig -} from "patternfly-ng"; - -@Component({ - encapsulation: ViewEncapsulation.None, - selector: "app-connection-table-selector", - templateUrl: "./connection-table-selector.component.html", - styleUrls: ["./connection-table-selector.component.css"] -}) -export class ConnectionTableSelectorComponent implements OnInit { - - private static readonly nameFilterId = "nameFilter"; - - @ViewChild("cellTemplate") public cellTemplate: TemplateRef< any >; - @ViewChild(RelationalTableSelectorComponent) public relationalTableSelector: RelationalTableSelectorComponent; - @Output() public selectedTableListUpdated: EventEmitter = new EventEmitter(); - - public columnDefinitions: any[]; - public filtersText = ""; - public filterConfig: FilterConfig; - public ngxConfig: NgxDataTableConfig; - public tableConfig: TableConfig; - public toolbarConfig: ToolbarConfig; - - private allConnections: Connection[] = []; - private filteredConnections: Connection[] = []; - - private connectionService: ConnectionService; - private wizardService: WizardService; - private selectedConn: Connection; - private connectionLoadingState: LoadingState = LoadingState.LOADING; - private logger: LoggerService; - - constructor( connectionService: ConnectionService, wizardService: WizardService, - logger: LoggerService ) { - this.connectionService = connectionService; - this.wizardService = wizardService; - this.logger = logger; - } - - /* - * Component initialization - */ - public ngOnInit(): void { - this.columnDefinitions = [ - { - cellTemplate: this.cellTemplate, - comparator: this.connectionComparator, - draggable: false, - name: "Connections", - prop: "name", - resizeable: false, - sortable: true, - width: "300" - } - ]; - - this.ngxConfig = { - footerHeight: 24, - messages: this.connectionsTableMessages, - selectionType: "single", - } as NgxDataTableConfig; - - this.filterConfig = { - fields: [ - { - id: ConnectionTableSelectorComponent.nameFilterId, - title: "Name", - placeholder: "Filter by name...", - type: FilterType.TEXT - } - ] as FilterField[], - appliedFilters: [], - resultsCount: this.filteredConnections.length, - totalCount: this.allConnections.length - } as FilterConfig; - - this.toolbarConfig = { - filterConfig: this.filterConfig - } as ToolbarConfig; - - this.tableConfig = { - toolbarConfig: this.toolbarConfig - } as TableConfig; - - // Load the connections - this.connectionLoadingState = LoadingState.LOADING; - const self = this; - this.connectionService - .getConnections(true, true) - .subscribe( - (connectionSummaries) => { - const conns = []; - for ( const connectionSummary of connectionSummaries ) { - const connStatus = connectionSummary.getStatus(); - const conn = connectionSummary.getConnection(); - conn.setStatus(connStatus); - conns.push(conn); - } - self.allConnections = conns; - self.filteredConnections = conns; - self.connectionLoadingState = LoadingState.LOADED_VALID; - if (self.wizardService.isEdit()) { - self.initEdit(); - } - }, - (error) => { - self.logger.error("[ConnectionTableSelectorComponent] Error getting connections: %o", error); - self.connectionLoadingState = LoadingState.LOADED_INVALID; - } - ); - } - - public filterChanged( $event: FilterEvent ): void { - this.filtersText = ""; - - $event.appliedFilters.forEach( ( filter ) => { - this.filtersText += filter.field.title + " : " + filter.value + "\n"; - } ); - - this.applyFilters( $event.appliedFilters ); - } - - // callback from connection table selection - public selectionChange( $event ): void { - const selected: Connection[] = $event.selected; - - // connection table is single select so use first element - const conn: Connection = selected[ 0 ]; - - // only set if schema selection has changed (see setter) - if ( this.selectedConn == null || this.selectedConn.name !== conn.name ) { - this.selectedConnection = conn; - } - } - - /** - * selector is valid if at least one table is selected. - * @returns {boolean} the selector status (true if one or more tables selected) - */ - public valid( ): boolean { - return this.getSelectedTables().length > 0; - } - - /** - * Determine if connections are loading - */ - public get connectionsLoading( ): boolean { - return this.connectionLoadingState === LoadingState.LOADING; - } - - /** - * Determine if connections are loaded and valid - */ - public get connectionsLoadedValid( ): boolean { - return this.connectionLoadingState === LoadingState.LOADED_VALID; - } - - /** - * Determine if connections are loaded and invalid - */ - public get connectionsLoadedInvalid( ): boolean { - return this.connectionLoadingState === LoadingState.LOADED_INVALID; - } - - /** - * Determine if the supplied connection is currently selected. - * @param {Connection} connection the connection - * @returns {boolean} true if the connection is selected - */ - public isConnectionSelected(connection: Connection): boolean { - return this.selectedConn && this.selectedConn === connection; - } - - /** - * Determine if a relational connection is currently selected - * @returns {boolean} true if a relational connection is selected - */ - public hasRelationalConnectionSelected(): boolean { - return (this.selectedConn && this.selectedConn.isJdbc()); - } - - /** - * Determine if a non-relational connection is currently selected - * @returns {boolean} true if a non-relational connection is selected - */ - public hasNonRelationalConnectionSelected(): boolean { - return (this.selectedConn && !this.selectedConn.isJdbc()); - } - - /** - * Determine if anything is selected - * @returns {boolean} true if a connection is selected - */ - public hasSelectedConnection( ): boolean { - return this.selectedConn != null; - } - - /** - * Get the currently selected Connection - * @returns {Connection} the current selection (may be null) - */ - public get selectedConnection( ): Connection { - return this.selectedConn; - } - - /** - * Set the currently selected Connection - * @param {Connection} conn the current selection (may be null) - */ - public set selectedConnection(conn: Connection) { - this.selectedConn = conn; - - // Set the specific selector with the current connection - if (this.relationalTableSelector) { - if (this.selectedConn && this.selectedConn.isJdbc()) { - this.relationalTableSelector.setConnection(this.selectedConnection); - } else { - this.relationalTableSelector.setConnection(null); - } - } - } - - /* - * Return all available Connections - * @returns {Connection[]} the list of all Connections - */ - public getAllConnections(): Connection[] { - return this.allConnections; - } - - /** - * Responds to table added event from the table selector. - * The table is added to the accumulator list. - * @param {ConnectionTable} addedTable the table to add to the accumulator list - */ - public onTableSelectionAdded(addedTable: ConnectionTable): void { - this.wizardService.addToSelectedConnectionTables(addedTable); - this.selectedTableListUpdated.emit(); - } - - /** - * Responds to table remove event from the table selector. - * The table is removed from the accumulator list, if found. - * @param {ConnectionTable} removedTable the table to remove from the accumulator list - */ - public onTableSelectionRemoved(removedTable: ConnectionTable): void { - const wasRemoved = this.wizardService.removeFromSelectedConnectionTables(removedTable); - if (wasRemoved) { - this.selectedTableListUpdated.emit(); - this.relationalTableSelector.deselectTable(removedTable); - } - } - - /* - * Determine if any tables are currently selected - * @returns {boolean} true if one or more tables are selected - */ - public get hasSelectedTables(): boolean { - const hasTables = this.getSelectedTables().length > 0; - return hasTables; - } - - /* - * Return all currently selected Tables - * @returns {ConnectionTable[]} the list of selected connection Tables - */ - public getSelectedTables(): ConnectionTable[] { - const connTables = this.wizardService.getSelectedConnectionTables(); - return connTables; - } - - public get connectionsTableMessages(): { emptyMessage: string; totalMessage: string | string } { - const numAll = this.allConnections.length; - const numFiltered = this.filteredConnections.length; - let msg: string; - - if ( numAll === numFiltered ) { - if ( this.filtersText.length === 0 ) { - msg = numAll === 1 ? "connection" : "connections"; - } else { - msg = numAll === 1 ? "matched connection" : "matched connections"; - } - } else { - msg = numFiltered === 1 ? "matched connection" : "matched connections"; - } - - return { - // message shows in an empty row in table - emptyMessage: "", - - // footer total message - totalMessage: msg - }; - } - - /** - * Called when the table is sorted. - * @param {string} thisName the connection name being sorted - * @param {string} thatName the connection name being compared to - * @returns {number} -1 if less than, 0 if equal, or 1 if greater than - */ - public connectionComparator( thisName: string, - thatName: string ): number { - return thisName.localeCompare( thatName ); - } - - private applyFilters( filters: Filter[] ): void { - this.filteredConnections = []; - - if ( filters && filters.length > 0 ) { - this.allConnections.forEach( ( item ) => { - if ( this.matchesFilters( item, filters ) ) { - this.filteredConnections.push( item ); - } - } ); - } else { - this.filteredConnections = this.allConnections; - } - - this.toolbarConfig.filterConfig.resultsCount = this.filteredConnections.length; - } - - private matchesFilter( item: any, - filter: Filter ): boolean { - let matches = true; - - if ( filter.field.id === ConnectionTableSelectorComponent.nameFilterId ) { - const pattern = "^" + filter.value.replace( "*", ".*" ); - matches = item.name.match( pattern ) !== null; - } - - return matches; - } - - private matchesFilters( item: any, - filters: Filter[] ): boolean { - let matches = true; - - filters.forEach( ( filter ) => { - if ( !this.matchesFilter( item, filter ) ) { - matches = false; - return matches; - } - }); - - return matches; - } - - /** - * Initialization for edit mode - */ - private initEdit(): void { - // Updates current connections on wizardService - this.wizardService.setCurrentConnections(this.allConnections); - - // Initialize the selected tables in the wizard service - this.wizardService.clearSelectedConnectionTables(); - const srcTables: string[] = this.wizardService.getSelectedDataservice().getServiceViewTables(); - for ( const tableStr of srcTables ) { - const subParts = tableStr.split("."); - const connectionName = subParts[0].replace(VdbsConstants.SCHEMA_VDB_SUFFIX, ""); - const tableName = subParts[1]; - let conn = this.wizardService.getCurrentConnection(connectionName); - if (!conn) { - conn = new Connection(); - conn.setId(connectionName); - } - const table: ConnectionTable = new ConnectionTable(); - table.setId(tableName); - table.setConnection(conn); - this.wizardService.addToSelectedConnectionTables(table); - } - this.selectedTableListUpdated.emit(); - - } - -} diff --git a/ngapp/src/app/dataservices/dataservices.component.ts b/ngapp/src/app/dataservices/dataservices.component.ts index a474ffbb..e12ed6ca 100644 --- a/ngapp/src/app/dataservices/dataservices.component.ts +++ b/ngapp/src/app/dataservices/dataservices.component.ts @@ -500,7 +500,7 @@ export class DataservicesComponent extends AbstractPageComponent implements OnIn */ public onNew(): void { this.wizardService.setEdit(false); - this.wizardService.clearSelectedConnectionTables(); + this.wizardService.clearSelectedSchemaNodes(); const link: string[] = [ DataservicesConstants.addDataservicePath ]; this.logger.log("[DataservicesPageComponent] Navigating to: %o", link); diff --git a/ngapp/src/app/dataservices/dataservices.module.ts b/ngapp/src/app/dataservices/dataservices.module.ts index f2ac3796..17e71899 100644 --- a/ngapp/src/app/dataservices/dataservices.module.ts +++ b/ngapp/src/app/dataservices/dataservices.module.ts @@ -37,14 +37,16 @@ import { VdbService } from "@dataservices/shared/vdb.service"; import { WizardService } from "@dataservices/shared/wizard.service"; import { environment } from "@environments/environment"; import { SharedModule } from "@shared/shared.module"; +import { TreeModule } from "angular-tree-component"; import { CodemirrorModule } from "ng2-codemirror"; import { PatternFlyNgModule } from "patternfly-ng"; import { AddDataserviceWizardComponent } from "./add-dataservice-wizard/add-dataservice-wizard.component"; import { AddDataserviceComponent } from "./add-dataservice/add-dataservice.component"; -import { ConnectionTableSelectorComponent } from "./connection-table-selector/connection-table-selector.component"; +import { ConnectionNodeSelectorComponent } from "./connection-node-selector/connection-node-selector.component"; +import { ConnectionSchemaTreeComponent } from "./connection-schema-tree/connection-schema-tree.component"; import { DataserviceCardComponent } from "./dataservices-cards/dataservice-card/dataservice-card.component"; -import { RelationalTableSelectorComponent } from "./relational-table-selector/relational-table-selector.component"; -import { SelectedTableComponent } from "./selected-table/selected-table.component"; +import { SelectedNodeComponent } from "./selected-node/selected-node.component"; +import { SelectedNodesListComponent } from "./selected-nodes-list/selected-nodes-list.component"; import { SqlControlComponent } from "./sql-control/sql-control.component"; import { TestDataserviceComponent } from "./test-dataservice/test-dataservice.component"; @@ -58,7 +60,8 @@ import { TestDataserviceComponent } from "./test-dataservice/test-dataservice.co ReactiveFormsModule, RouterModule, PatternFlyNgModule, - CodemirrorModule + CodemirrorModule, + TreeModule ], declarations: [ DataservicesDetailsComponent, @@ -68,12 +71,13 @@ import { TestDataserviceComponent } from "./test-dataservice/test-dataservice.co DataservicesListComponent, AddDataserviceWizardComponent, AddDataserviceComponent, - ConnectionTableSelectorComponent, TestDataserviceComponent, SqlControlComponent, - SelectedTableComponent, + SelectedNodeComponent, DataserviceCardComponent, - RelationalTableSelectorComponent + ConnectionSchemaTreeComponent, + SelectedNodesListComponent, + ConnectionNodeSelectorComponent ], providers: [ { diff --git a/ngapp/src/app/dataservices/relational-table-selector/relational-table-selector.component.css b/ngapp/src/app/dataservices/relational-table-selector/relational-table-selector.component.css deleted file mode 100644 index 8e4b5b0f..00000000 --- a/ngapp/src/app/dataservices/relational-table-selector/relational-table-selector.component.css +++ /dev/null @@ -1,32 +0,0 @@ -.alert-padding { - padding-top: 10px; -} - -.jdbc-column-results { - padding-left: 0; - padding-right: 0; -} - -.jdbc-column-results .filter-pf .form-group { - width: auto !important; -} - -.jdbc-column-results .toolbar-pf .form-group { - border-right: none; -} - -.jdbc-column-title { - height: 20px; - border:1px solid white; - background-color: #dddddd; - padding-top: 0; - padding-left: 10px; - padding-right: 0; -} - -.list-div { - position: relative; - height: 100%; - max-height: 300px; - overflow-y: auto; -} diff --git a/ngapp/src/app/dataservices/relational-table-selector/relational-table-selector.component.html b/ngapp/src/app/dataservices/relational-table-selector/relational-table-selector.component.html deleted file mode 100644 index 9470ef2d..00000000 --- a/ngapp/src/app/dataservices/relational-table-selector/relational-table-selector.component.html +++ /dev/null @@ -1,31 +0,0 @@ - - - -
    - -
    -
    -
    - - Unable to load tables -
    -
    -
    -
    - - No tables available -
    -
    -
    - - -
    - - {{ row.getId() }} - diff --git a/ngapp/src/app/dataservices/relational-table-selector/relational-table-selector.component.spec.ts b/ngapp/src/app/dataservices/relational-table-selector/relational-table-selector.component.spec.ts deleted file mode 100644 index d03871a1..00000000 --- a/ngapp/src/app/dataservices/relational-table-selector/relational-table-selector.component.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { async, ComponentFixture, TestBed } from "@angular/core/testing"; - -import { FormsModule } from "@angular/forms"; -import { HttpModule } from "@angular/http"; -import { Connection } from "@connections/shared/connection.model"; -import { ConnectionService } from "app/connections/shared/connection.service"; -import { MockConnectionService } from "app/connections/shared/mock-connection.service"; -import { AppSettingsService } from "app/core/app-settings.service"; -import { LoggerService } from "app/core/logger.service"; -import { MockAppSettingsService } from "app/core/mock-app-settings.service"; -import { RelationalTableSelectorComponent } from "app/dataservices/relational-table-selector/relational-table-selector.component"; -import { SelectedTableComponent } from "app/dataservices/selected-table/selected-table.component"; -import { MockVdbService } from "app/dataservices/shared/mock-vdb.service"; -import { NotifierService } from "app/dataservices/shared/notifier.service"; -import { VdbService } from "app/dataservices/shared/vdb.service"; -import { WizardService } from "app/dataservices/shared/wizard.service"; -import { PatternFlyNgModule } from "patternfly-ng"; - -describe("RelationalTableSelectorComponent", () => { - let component: RelationalTableSelectorComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ FormsModule, HttpModule, PatternFlyNgModule ], - declarations: [ RelationalTableSelectorComponent, SelectedTableComponent ], - providers: [ - AppSettingsService, LoggerService, NotifierService, WizardService, - { provide: AppSettingsService, useClass: MockAppSettingsService }, - { provide: ConnectionService, useClass: MockConnectionService }, - { provide: VdbService, useClass: MockVdbService }, - ] - }) - .compileComponents().then(() => { - // nothing to do - }); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(RelationalTableSelectorComponent); - component = fixture.componentInstance; - const conn: Connection = new Connection(); - conn.setId("conn1"); - component.connection = conn; - component.setConnection(conn); - fixture.detectChanges(); - }); - - it("should be created", () => { - console.log("========== [RelationalTableSelectorComponent] should be created"); - expect(component).toBeTruthy(); - }); - -}); diff --git a/ngapp/src/app/dataservices/relational-table-selector/relational-table-selector.component.ts b/ngapp/src/app/dataservices/relational-table-selector/relational-table-selector.component.ts deleted file mode 100644 index dbe9f2ad..00000000 --- a/ngapp/src/app/dataservices/relational-table-selector/relational-table-selector.component.ts +++ /dev/null @@ -1,394 +0,0 @@ -/** - * @license - * Copyright 2017 JBoss Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Component, OnInit, TemplateRef, ViewChild, ViewEncapsulation } from "@angular/core"; -import { Input } from "@angular/core"; -import { EventEmitter } from "@angular/core"; -import { Output } from "@angular/core"; -import { ConnectionTable } from "@connections/shared/connection-table.model"; -import { Connection } from "app/connections/shared/connection.model"; -import { ConnectionService } from "app/connections/shared/connection.service"; -import { LoggerService } from "app/core/logger.service"; -import { TableSelector } from "app/dataservices/shared/table-selector"; -import { WizardService } from "app/dataservices/shared/wizard.service"; -import { LoadingState } from "app/shared/loading-state.enum"; -import { - Filter, - FilterConfig, FilterField, FilterType, NgxDataTableConfig, TableConfig, TableEvent, - ToolbarConfig -} from "patternfly-ng"; - -@Component({ - encapsulation: ViewEncapsulation.None, - selector: "app-relational-table-selector", - templateUrl: "./relational-table-selector.component.html", - styleUrls: ["./relational-table-selector.component.css"] -}) - -export class RelationalTableSelectorComponent implements OnInit, TableSelector { - - private static readonly tableFilterId = "tableFilter"; - - @ViewChild("tableCellTemplate") public tableCellTemplate: TemplateRef< any >; - @Input() public connection: Connection; - @Output() public tableSelectionAdded: EventEmitter = new EventEmitter(); - @Output() public tableSelectionRemoved: EventEmitter = new EventEmitter(); - - public tableColumns: any[]; - public tableFiltersText = ""; - public tableFilterConfig: FilterConfig; - public ngxTableConfig: NgxDataTableConfig; - public tableTableConfig: TableConfig; - public tableToolbarConfig: ToolbarConfig; - - public selectedAllRows = false; - - private connectionService: ConnectionService; - private wizardService: WizardService; - private logger: LoggerService; - private tables: ConnectionTable[] = []; - private filteredTables: ConnectionTable[] = []; - private tableFilter = ""; - private tableLoadingState: LoadingState = LoadingState.LOADING; - - constructor(connectionService: ConnectionService, wizardService: WizardService, logger: LoggerService ) { - this.connectionService = connectionService; - this.wizardService = wizardService; - this.logger = logger; - } - - public ngOnInit(): void { - this.tableColumns = [ - { - cellTemplate: this.tableCellTemplate, - comparator: "nameComparator", - draggable: false, - name: "Tables", - prop: "keng__id", - resizeable: false, - sortable: true, - width: "300" - } - ]; - - this.ngxTableConfig = { - footerHeight: 24, - messages: this.tableTableMessages, - selectionType: "checkbox", - } as NgxDataTableConfig; - - this.tableFilterConfig = { - fields: [ - { - id: RelationalTableSelectorComponent.tableFilterId, - title: "Name", - placeholder: "Filter by name...", - type: FilterType.TEXT - } - ] as FilterField[], - appliedFilters: [], - resultsCount: this.filteredTables.length, - totalCount: this.tables.length - } as FilterConfig; - - this.tableToolbarConfig = { - filterConfig: this.tableFilterConfig - } as ToolbarConfig; - - this.tableTableConfig = { - showCheckbox: true, - toolbarConfig: this.tableToolbarConfig - } as TableConfig; - - // Load the connection tables for a connection - this.setConnection(this.connection); - } - - /* - * Set the connection for this relational table selector. Setting the connection triggers loading - * of the tables. - * @param {Connection} conn the relational connection - */ - public setConnection(conn: Connection): void { - // Load the tables for the connection - this.tables = []; - this.filteredTables = []; - this.selectedAllRows = false; - this.tableLoadingState = LoadingState.LOADING; - const self = this; - this.connectionService - .getConnectionTables(conn.getId()) - .subscribe( - (connTables) => { - for ( const table of connTables ) { - table.setConnection(conn); - self.tables.push(table); - self.filteredTables.push(table); - } - // select any of the tables that are already selected - self.setInitialTableSelections(); - self.tableLoadingState = LoadingState.LOADED_VALID; - }, - (error) => { - self.logger.error("[RelationalTableSelectorComponent] Error getting tables: %o", error); - self.tableLoadingState = LoadingState.LOADED_INVALID; - } - ); - } - - /** - * Called when the table is sorted. - * @param {string} thisName the name being sorted - * @param {string} thatName the name being compared to - * @returns {number} -1 if less than, 0 if equal, or 1 if greater than - */ - public nameComparator( thisName: string, - thatName: string ): number { - return thisName.localeCompare( thatName ); - } - - /** - * Determine if tables are loading - */ - public get tablesLoading( ): boolean { - return this.tableLoadingState === LoadingState.LOADING; - } - - /** - * Determine if tables are loaded, valid and not empty - */ - public get tablesLoadedValidNotEmpty( ): boolean { - return (this.tableLoadingState === LoadingState.LOADED_VALID) && this.tables.length > 0; - } - - /** - * Determine if tables are loaded, valid but empty - */ - public get tablesLoadedValidEmpty( ): boolean { - return (this.tableLoadingState === LoadingState.LOADED_VALID) && this.tables.length === 0; - } - - /** - * Determine if tables are loaded and invalid - */ - public get tablesLoadedInvalid( ): boolean { - return this.tableLoadingState === LoadingState.LOADED_INVALID; - } - - public get tableTableMessages(): { emptyMessage: string; totalMessage: string | string } { - const numAll = this.tables.length; - const numFiltered = this.filteredTables.length; - let msg: string; - - if ( numAll === numFiltered ) { - if ( this.tableFilter.length === 0 ) { - msg = numAll === 1 ? "table" : "tables"; - } else { - msg = numAll === 1 ? "matched table" : "matched tables"; - } - } else { - msg = numFiltered === 1 ? "matched table" : "matched tables"; - } - - return { - // message shows in an empty row in table - emptyMessage: "", - - // footer total message - totalMessage: msg - }; - } - - /** - * Callback when key is pressed in table column filter. - */ - public tableFilterChanged( $event ): void { - this.tableFiltersText = ""; - - $event.appliedFilters.forEach( ( filter ) => { - this.tableFiltersText += filter.field.title + " : " + filter.value + "\n"; - } ); - - this.applyTableFilters( $event.appliedFilters ); - } - - /* - * Get all tables - * @returns {ConnectionTable[]} the current tables for the selected schema - */ - public getTables(): ConnectionTable[] { - return this.tables; - } - - /* - * Determine if any tables are currently selected - * @returns {boolean} true if one or more tables are selected - */ - public hasSelectedTables(): boolean { - for ( const table of this.tables ) { - if ( table.selected ) { - return true; - } - } - - return false; - } - - /* - * Get the array of currently selected ConnectionTables - * @returns {ConnectionTable[]} the array of selected ConnectionTables - */ - public getSelectedTables(): ConnectionTable[] { - return this.tables.filter( ( table ) => table.selected ); - } - - /* - * Handler for changes in table selection - */ - public selectedTableChanged( $event: TableEvent ): void { - const table: ConnectionTable = $event.row; - - if ( table ) { - if ( table.selected ) { - this.tableSelectionAdded.emit( table ); - } else { - this.tableSelectionRemoved.emit( table ); - } - } else { - if ( $event.selectedRows.length === 0 ) { - this.tables.forEach( ( tbl ) => { - this.tableSelectionRemoved.emit( tbl ); - } ); - } else { - this.tables.forEach( ( tbl ) => { - this.tableSelectionAdded.emit( tbl ); - } ); - } - } - } - - /** - * Deselects the table if one with a matching name and connection is currently selected - * @param {ConnectionTable} table - */ - public deselectTable(table: ConnectionTable): void { - const connName = table.getConnection().getId(); - const tableName = table.getId(); - - for (const theTable of this.tables) { - const theConnName = theTable.getConnection().getId(); - const theTableName = theTable.getId(); - if (theConnName === connName && theTableName === tableName) { - theTable.selected = false; - - // need to set column header checkbox state - let enable = true; - - for ( const filteredTable of this.filteredTables ) { - if ( !filteredTable.selected ) { - enable = false; - break; - } - } - - if ( this.selectedAllRows !== enable ) { - this.selectedAllRows = enable; - } - - break; - } - } - } - - private applyTableFilters( filters: Filter[] ): void { - this.filteredTables = []; - - if ( filters && filters.length > 0 ) { - this.tables.forEach( ( item ) => { - if ( this.matchesFilters( item, filters ) ) { - this.filteredTables.push( item ); - } - } ); - } else { - this.filteredTables = this.tables; - } - - this.tableToolbarConfig.filterConfig.resultsCount = this.filteredTables.length; - } - - private isSelected( selectedTables: ConnectionTable[], - table: ConnectionTable ): boolean { - for ( const selected of selectedTables ) { - if ( selected.getId() === table.getId() ) { - return true; - } - } - - return false; - } - - private matchesFilter( item: any, - filter: Filter ): boolean { - let matches = true; - - if ( filter.field.id === RelationalTableSelectorComponent.tableFilterId ) { - const pattern = "^" + filter.value.replace( "*", ".*" ); - matches = item.name.match( pattern ) !== null; - } - - return matches; - } - - private matchesFilters( item: any, - filters: Filter[] ): boolean { - let matches = true; - - filters.forEach( ( filter ) => { - if ( !this.matchesFilter( item, filter ) ) { - matches = false; - return matches; - } - }); - - return matches; - } - - private setInitialTableSelections(): void { - let enableSelectAll = true; - - for ( const table of this.tables ) { - const tableName = table.getId(); - const connName = table.getConnection().getId(); - - for ( const initialTable of this.wizardService.getSelectedConnectionTables() ) { - const iTableName = initialTable.getId(); - const iConnName = initialTable.getConnection().getId(); - if (iConnName === connName && iTableName === tableName ) { - table.selected = true; - break; - } - } - - if ( !table.selected ) { - enableSelectAll = false; - } - } - - this.selectedAllRows = enableSelectAll; - } - -} diff --git a/ngapp/src/app/dataservices/selected-table/column.ts b/ngapp/src/app/dataservices/selected-node/column.ts similarity index 100% rename from ngapp/src/app/dataservices/selected-table/column.ts rename to ngapp/src/app/dataservices/selected-node/column.ts diff --git a/ngapp/src/app/dataservices/selected-table/selected-table.component.css b/ngapp/src/app/dataservices/selected-node/selected-node.component.css similarity index 100% rename from ngapp/src/app/dataservices/selected-table/selected-table.component.css rename to ngapp/src/app/dataservices/selected-node/selected-node.component.css diff --git a/ngapp/src/app/dataservices/selected-table/selected-table.component.html b/ngapp/src/app/dataservices/selected-node/selected-node.component.html similarity index 94% rename from ngapp/src/app/dataservices/selected-table/selected-table.component.html rename to ngapp/src/app/dataservices/selected-node/selected-node.component.html index d0001f7b..e5130126 100644 --- a/ngapp/src/app/dataservices/selected-table/selected-table.component.html +++ b/ngapp/src/app/dataservices/selected-node/selected-node.component.html @@ -6,11 +6,11 @@
    - {{ table.getId() }} + {{ node.getName() }}
    - {{ table.getConnection().getId() }} + {{ node.getConnectionName() }}
    diff --git a/ngapp/src/app/dataservices/selected-node/selected-node.component.spec.ts b/ngapp/src/app/dataservices/selected-node/selected-node.component.spec.ts new file mode 100644 index 00000000..d246a35c --- /dev/null +++ b/ngapp/src/app/dataservices/selected-node/selected-node.component.spec.ts @@ -0,0 +1,54 @@ +import { async, ComponentFixture, TestBed } from "@angular/core/testing"; +import { SchemaNode } from "@connections/shared/schema-node.model"; +import { PatternFlyNgModule } from "patternfly-ng"; +import { SelectedNodeComponent } from "./selected-node.component"; + +describe("SelectedNodeComponent", () => { + let component: SelectedNodeComponent; + let fixture: ComponentFixture; + let htmlElem: HTMLElement; + + const nodeName = "testNode"; + const connectionName = "testConnection"; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ PatternFlyNgModule ], + declarations: [ SelectedNodeComponent ] + }) + .compileComponents().then(() => { + // nothing to do + }); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SelectedNodeComponent); + component = fixture.componentInstance; + htmlElem = fixture.nativeElement; + + const node = new SchemaNode(); + node.setConnectionName(connectionName); + node.setName(nodeName); + component.node = node; + + fixture.detectChanges(); + }); + + it("should be created", () => { + console.log("========== [SelectedNodeComponent] should be created"); + expect(component).toBeTruthy(); + }); + + it("should have correct table name", () => { + console.log("========== [SelectedNodeComponent] should have correct table name"); + const heading = fixture.debugElement.nativeElement.querySelector( ".object-card-title" ); + expect( heading.textContent ).toContain( nodeName ); + }); + + it("should have correct connector name", () => { + console.log("========== [SelectedNodeComponent] should have correct connector name"); + // const heading = fixture.debugElement.nativeElement.querySelector( ".text-info" ); + // expect( heading.textContent ).toContain( connectionName ); + }); + +}); diff --git a/ngapp/src/app/dataservices/selected-table/selected-table.component.ts b/ngapp/src/app/dataservices/selected-node/selected-node.component.ts similarity index 82% rename from ngapp/src/app/dataservices/selected-table/selected-table.component.ts rename to ngapp/src/app/dataservices/selected-node/selected-node.component.ts index 97d751a7..0e4f872f 100644 --- a/ngapp/src/app/dataservices/selected-table/selected-table.component.ts +++ b/ngapp/src/app/dataservices/selected-node/selected-node.component.ts @@ -16,20 +16,20 @@ */ import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from "@angular/core"; -import { ConnectionTable } from "@connections/shared/connection-table.model"; -import { Column } from "@dataservices/selected-table/column"; +import { SchemaNode } from "@connections/shared/schema-node.model"; +import { Column } from "@dataservices/selected-node/column"; import { CardAction, CardConfig, TableConfig, TableEvent } from "patternfly-ng"; @Component({ encapsulation: ViewEncapsulation.None, - selector: "app-selected-table", - templateUrl: "./selected-table.component.html", - styleUrls: ["./selected-table.component.css"] + selector: "app-selected-node", + templateUrl: "./selected-node.component.html", + styleUrls: ["./selected-node.component.css"] }) -export class SelectedTableComponent implements OnInit { +export class SelectedNodeComponent implements OnInit { - @Input() public table: ConnectionTable; - @Output() public selectionListTableRemoved: EventEmitter = new EventEmitter(); + @Input() public node: SchemaNode; + @Output() public selectionNodeRemoved: EventEmitter = new EventEmitter(); public config: CardConfig; public columnDefinitions: any[]; @@ -100,8 +100,8 @@ export class SelectedTableComponent implements OnInit { } public onRemove(): void { - this.table.selected = false; - this.selectionListTableRemoved.emit(this.table); + this.node.selected = false; + this.selectionNodeRemoved.emit(this.node); } } diff --git a/ngapp/src/app/dataservices/selected-nodes-list/selected-nodes-list.component.css b/ngapp/src/app/dataservices/selected-nodes-list/selected-nodes-list.component.css new file mode 100644 index 00000000..3027fd8a --- /dev/null +++ b/ngapp/src/app/dataservices/selected-nodes-list/selected-nodes-list.component.css @@ -0,0 +1,10 @@ +.alert-padding { + padding-top: 10px; +} + +.selected-tables-container { + background-color: #bbbbbb; + max-height: 480px; + margin-top: 0; + overflow-y: auto; +} diff --git a/ngapp/src/app/dataservices/selected-nodes-list/selected-nodes-list.component.html b/ngapp/src/app/dataservices/selected-nodes-list/selected-nodes-list.component.html new file mode 100644 index 00000000..e66f2e9c --- /dev/null +++ b/ngapp/src/app/dataservices/selected-nodes-list/selected-nodes-list.component.html @@ -0,0 +1,11 @@ +
    +
    + + No source nodes selected +
    +
    +
    +
    + +
    +
    diff --git a/ngapp/src/app/dataservices/selected-nodes-list/selected-nodes-list.component.spec.ts b/ngapp/src/app/dataservices/selected-nodes-list/selected-nodes-list.component.spec.ts new file mode 100644 index 00000000..53c71b41 --- /dev/null +++ b/ngapp/src/app/dataservices/selected-nodes-list/selected-nodes-list.component.spec.ts @@ -0,0 +1,28 @@ +import { async, ComponentFixture, TestBed } from "@angular/core/testing"; + +import { SelectedNodeComponent } from "@dataservices/selected-node/selected-node.component"; +import { PatternFlyNgModule } from "patternfly-ng"; +import { SelectedNodesListComponent } from "./selected-nodes-list.component"; + +describe("SelectedNodesListComponent", () => { + let component: SelectedNodesListComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ PatternFlyNgModule ], + declarations: [ SelectedNodesListComponent, SelectedNodeComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SelectedNodesListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should be created", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ngapp/src/app/dataservices/selected-nodes-list/selected-nodes-list.component.ts b/ngapp/src/app/dataservices/selected-nodes-list/selected-nodes-list.component.ts new file mode 100644 index 00000000..04582059 --- /dev/null +++ b/ngapp/src/app/dataservices/selected-nodes-list/selected-nodes-list.component.ts @@ -0,0 +1,55 @@ +import { Component, EventEmitter, OnInit, Output } from "@angular/core"; +import { SchemaNode } from "@connections/shared/schema-node.model"; + +@Component({ + selector: "app-selected-nodes-list", + templateUrl: "./selected-nodes-list.component.html", + styleUrls: ["./selected-nodes-list.component.css"] +}) +export class SelectedNodesListComponent implements OnInit { + + @Output() public selectionListNodeRemoved: EventEmitter = new EventEmitter(); + + private nodes: SchemaNode[] = []; + + constructor() { + // nothing to do + } + + public ngOnInit(): void { + // nothing to do + } + + public setNodes( nodes: SchemaNode[] ): void { + this.nodes = nodes; + } + + public getNodes( ): SchemaNode[] { + return this.nodes; + } + + public get hasNodes( ): boolean { + return this.getNodes().length > 0; + } + + /** + * Determine if the list already has the specified node + * @param {SchemaNode} node + * @returns {boolean} + */ + public hasNode( node: SchemaNode ): boolean { + let hasIt = false; + for (const listNode of this.nodes) { + if ( (listNode.getName() === node.getName()) && + (listNode.getConnectionName() === node.getConnectionName()) ) { + hasIt = true; + break; + } + } + return hasIt; + } + + public onNodeRemoved( node: SchemaNode ): void { + this.selectionListNodeRemoved.emit(node); + } +} diff --git a/ngapp/src/app/dataservices/selected-table/selected-table.component.spec.ts b/ngapp/src/app/dataservices/selected-table/selected-table.component.spec.ts deleted file mode 100644 index 97750a85..00000000 --- a/ngapp/src/app/dataservices/selected-table/selected-table.component.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { async, ComponentFixture, TestBed } from "@angular/core/testing"; -import { ConnectionTable } from "@connections/shared/connection-table.model"; -import { Connection } from "@connections/shared/connection.model"; -import { PatternFlyNgModule } from "patternfly-ng"; -import { SelectedTableComponent } from "./selected-table.component"; - -describe("SelectedTableComponent", () => { - let component: SelectedTableComponent; - let fixture: ComponentFixture; - let htmlElem: HTMLElement; - - const connectionName = "testConnection"; - const tableName = "testTable"; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ PatternFlyNgModule ], - declarations: [ SelectedTableComponent ] - }) - .compileComponents().then(() => { - // nothing to do - }); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(SelectedTableComponent); - component = fixture.componentInstance; - htmlElem = fixture.nativeElement; - - const connection = new Connection(); - connection.setId( connectionName ); - - const table = new ConnectionTable(); - table.setConnection(connection); - table.setId(tableName); - component.table = table; - - fixture.detectChanges(); - }); - - it("should be created", () => { - console.log("========== [SelectedTableComponent] should be created"); - expect(component).toBeTruthy(); - }); - - it("should have correct table name", () => { - const heading = fixture.debugElement.nativeElement.querySelector( ".object-card-title" ); - expect( heading.textContent ).toContain( tableName ); - }); - - it("should have correct connector name", () => { - const heading = fixture.debugElement.nativeElement.querySelector( ".text-info" ); - expect( heading.textContent ).toContain( connectionName ); - }); - -}); diff --git a/ngapp/src/app/dataservices/shared/dataservice.service.ts b/ngapp/src/app/dataservices/shared/dataservice.service.ts index 60c6a625..a610a11e 100644 --- a/ngapp/src/app/dataservices/shared/dataservice.service.ts +++ b/ngapp/src/app/dataservices/shared/dataservice.service.ts @@ -17,8 +17,8 @@ import { Injectable } from "@angular/core"; import { Http } from "@angular/http"; -import { ConnectionTable } from "@connections/shared/connection-table.model"; import { Connection } from "@connections/shared/connection.model"; +import { SchemaNode } from "@connections/shared/schema-node.model"; import { ApiService } from "@core/api.service"; import { AppSettingsService } from "@core/app-settings.service"; import { LoggerService } from "@core/logger.service"; @@ -333,12 +333,15 @@ export class DataserviceService extends ApiService { /** * Create a dataservice which is a straight passthru to the supplied tables * @param {NewDataservice} dataservice - * @param {ConnectionTable[]} connectionTables + * @param {SchemaNode[]} schemaNodes the source 'tables' for the service + * @param {Connection[]} connections the required connections for the schemaNodes * @returns {Observable} */ - public createDataserviceForSingleSourceTables(dataservice: NewDataservice, connectionTables: ConnectionTable[]): Observable { - // All tables from same connection - const connection: Connection = connectionTables[0].getConnection(); + public createDataserviceForSingleSourceTables(dataservice: NewDataservice, + schemaNodes: SchemaNode[], + connections: Connection[]): Observable { + // All tables currently must be from same connection + const connection: Connection = connections[0]; const schemaVdbName = connection.schemaVdbName; const schemaVdbModelName = connection.schemaVdbModelName; const schemaVdbModelSourceName = connection.schemaVdbModelSourceName; @@ -348,8 +351,8 @@ export class DataserviceService extends ApiService { // Get table paths for the tables used in the service const tablePaths = []; - for ( const connectionTable of connectionTables ) { - const tablePath = vdbPath + "/" + schemaVdbModelName + "/" + connectionTable.getId(); + for ( const connectionNode of schemaNodes ) { + const tablePath = vdbPath + "/" + schemaVdbModelName + "/" + connectionNode.getName(); tablePaths.push(tablePath); } @@ -369,12 +372,15 @@ export class DataserviceService extends ApiService { * Updates a dataservice with single table source. This is simply a create, with the added step of * deleting the existing workspace dataservice first. * @param {NewDataservice} dataservice - * @param {ConnectionTable[]} sourceTables + * @param {SchemaNode[]} schemaNodes + * @param {Connection[]} connections * @returns {Observable} */ - public updateDataserviceForSingleSourceTables(dataservice: NewDataservice, sourceTables: ConnectionTable[]): Observable { + public updateDataserviceForSingleSourceTables(dataservice: NewDataservice, + schemaNodes: SchemaNode[], + connections: Connection[]): Observable { return this.deleteDataservice(dataservice.getId()) - .flatMap((res) => this.createDataserviceForSingleSourceTables(dataservice, sourceTables)); + .flatMap((res) => this.createDataserviceForSingleSourceTables(dataservice, schemaNodes, connections)); } /** diff --git a/ngapp/src/app/dataservices/shared/node-selector.ts b/ngapp/src/app/dataservices/shared/node-selector.ts new file mode 100644 index 00000000..8936bd3a --- /dev/null +++ b/ngapp/src/app/dataservices/shared/node-selector.ts @@ -0,0 +1,43 @@ +/** + * @license + * Copyright 2017 JBoss Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SchemaNode } from "@connections/shared/schema-node.model"; + +/** + * The node selector interface + */ +export interface NodeSelector { + + /** + * Determine if any nodes are currently selected + * @returns {boolean} true if one or more nodes are selected + */ + hasSelectedNodes( ): boolean; + + /** + * Get the array of currently selected SchemaNodes + * @returns {SchemaNode[]} the array of selected SchemaNodes (never null, but may be empty) + */ + getSelectedNodes(): SchemaNode[]; + + /** + * Deselect the node if a node with the same name is currently selected. + * @param {SchemaNode} node the table to deselect + */ + deselectNode(node: SchemaNode): void; + +} diff --git a/ngapp/src/app/dataservices/shared/table-selector.ts b/ngapp/src/app/dataservices/shared/table-selector.ts deleted file mode 100644 index d43e9def..00000000 --- a/ngapp/src/app/dataservices/shared/table-selector.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @license - * Copyright 2017 JBoss Inc - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { ConnectionTable } from "@connections/shared/connection-table.model"; -import { Connection } from "@connections/shared/connection.model"; - -/** - * The table selector interface - */ -export interface TableSelector { - - /** - * Set the connection for this jdbc table selector - * @param {Connection} conn the jdbc connection - */ - setConnection(conn: Connection): void; - - /** - * Determine if any tables are currently selected - * @returns {boolean} true if one or more tables are selected - */ - hasSelectedTables( ): boolean; - - /** - * Get the array of currently selected Tables - * @returns {ConnectionTable[]} the array of selected ConnectionTable (never null, but may be empty) - */ - getSelectedTables(): ConnectionTable[]; - - /** - * Deselect the table if a table with the same name and connection is currently selected. - * @param {ConnectionTable} table the table to deselect - */ - deselectTable(table: ConnectionTable): void; - -} diff --git a/ngapp/src/app/dataservices/shared/wizard.service.ts b/ngapp/src/app/dataservices/shared/wizard.service.ts index 9e78c028..8b09154f 100644 --- a/ngapp/src/app/dataservices/shared/wizard.service.ts +++ b/ngapp/src/app/dataservices/shared/wizard.service.ts @@ -1,6 +1,6 @@ import { Injectable } from "@angular/core"; -import { ConnectionTable } from "@connections/shared/connection-table.model"; import { Connection } from "@connections/shared/connection.model"; +import { SchemaNode } from "@connections/shared/schema-node.model"; import { Dataservice } from "@dataservices/shared/dataservice.model"; @Injectable() @@ -8,7 +8,7 @@ export class WizardService { private static newlyAddedServiceWaitMillis = 10000; // Wait of 10 sec - private selectedConnectionTables: ConnectionTable[] = []; + private selectedSchemaNodes: SchemaNode[] = []; private edit = false; private currentConnections: Connection[] = []; private selectedDataservice: Dataservice; @@ -70,52 +70,53 @@ export class WizardService { } /** - * Get the wizard table selections - * @returns {ConnectionTable[]} the selections + * Get the wizard node selections + * @returns {SchemaNode[]} the selections */ - public getSelectedConnectionTables( ): ConnectionTable[] { - return this.selectedConnectionTables.sort( - ( thisTable, thatTable ) => { - return thisTable.getId().localeCompare( thatTable.getId() ); + public getSelectedSchemaNodes( ): SchemaNode[] { + const sel = this.selectedSchemaNodes.sort( + ( thisNode, thatNode ) => { + return thisNode.getName().localeCompare( thatNode.getName() ); } ); + return sel; } /** - * Clears the list of selected connection tables + * Clears the list of selected schema nodes */ - public clearSelectedConnectionTables( ): void { - this.selectedConnectionTables = []; + public clearSelectedSchemaNodes( ): void { + this.selectedSchemaNodes = []; } /** - * Determine if the supplied table is one of the current selections - * @param {ConnectionTable} table the table + * Determine if the supplied node is one of the current selections + * @param {SchemaNode} node the node */ - public isConnectionTableSelected(table: ConnectionTable): boolean { - return this.getConnectionTableIndex(table) > -1; + public isSchemaNodeSelected(node: SchemaNode): boolean { + return this.getSchemaNodeIndex(node) > -1; } /** - * Add a table to the currently selected connection tables - * @param {ConnectionTable} tableToAdd table to add + * Add a node to the currently selected connection node + * @param {SchemaNode} nodeToAdd node to add */ - public addToSelectedConnectionTables(tableToAdd: ConnectionTable): void { - if (!this.isConnectionTableSelected(tableToAdd)) { - this.selectedConnectionTables.push(tableToAdd); + public addToSelectedSchemaNodes(nodeToAdd: SchemaNode): void { + if (!this.isSchemaNodeSelected(nodeToAdd)) { + this.selectedSchemaNodes.push(nodeToAdd); } } /** - * Remove a table from the currently selected connection tables - * @param {ConnectionTable} tableToRemove - * @returns {boolean} + * Remove a node from the currently selected schema nodes + * @param {SchemaNode} nodeToRemove the node to remove + * @returns {boolean} true if node was removed */ - public removeFromSelectedConnectionTables(tableToRemove: ConnectionTable): boolean { + public removeFromSelectedSchemaNodes(nodeToRemove: SchemaNode): boolean { let wasRemoved = false; - const index = this.getConnectionTableIndex(tableToRemove); + const index = this.getSchemaNodeIndex(nodeToRemove); if (index > -1) { - this.selectedConnectionTables.splice(index, 1); + this.selectedSchemaNodes.splice(index, 1); wasRemoved = true; } @@ -210,19 +211,20 @@ export class WizardService { } /** - * Find index of the connection table in the wizard selected tables list. -1 if not found - * @param {ConnectionTable} table connection table - * @returns {number} + * Find index of the node in the selected nodes list. -1 if not found + * @param {SchemaNode} node the node + * @returns {number} the node index */ - private getConnectionTableIndex(table: ConnectionTable): number { + private getSchemaNodeIndex(node: SchemaNode): number { // supplied table and connection - const connName = table.getConnection().getId(); - const tableName = table.getId(); + const nodeName = node.getName(); + // const nodePath = node.getPath(); let i = 0; - for (const wizTable of this.selectedConnectionTables) { - const wizTableName = wizTable.getId(); - const wizConnName = wizTable.getConnection().getId(); - if (wizTableName === tableName && wizConnName === connName) { + for (const wizTable of this.selectedSchemaNodes) { + const wizNodeName = wizTable.getName(); + // const wizNodePath = wizTable.getPath(); + if (wizNodeName === nodeName) { +// if (wizNodeName === nodeName && wizNodePath === nodePath) { return i; } i++; diff --git a/ngapp/src/app/shared/test-data.service.ts b/ngapp/src/app/shared/test-data.service.ts index d7e6d53f..d3915f1a 100644 --- a/ngapp/src/app/shared/test-data.service.ts +++ b/ngapp/src/app/shared/test-data.service.ts @@ -17,9 +17,8 @@ import { Injectable } from "@angular/core"; import { ConnectionStatus } from "@connections/shared/connection-status"; -import { ConnectionTable } from "@connections/shared/connection-table.model"; import { Connection } from "@connections/shared/connection.model"; -import { SchemaInfo } from "@connections/shared/schema-info.model"; +import { SchemaNode } from "@connections/shared/schema-node.model"; import { ServiceCatalogSource } from "@connections/shared/service-catalog-source.model"; import { ConnectionSummary } from "@dataservices/shared/connection-summary.model"; import { Dataservice } from "@dataservices/shared/dataservice.model"; @@ -126,7 +125,7 @@ export class TestDataService { } ); - private static vdbStatuses = + private static readonly vdbStatuses = { "keng__baseUri": "http://das-beetle-studio.192.168.42.142.nip.io/vdb-builder/v1/", "vdbs": [ @@ -419,30 +418,99 @@ export class TestDataService { ]; // ================================================================= - // ConnectionTables for the connections + // Schemas for the connections // ================================================================= - private static pgConnConnectionTables = [ - TestDataService.createConnectionTable("pgTable1", TestDataService.pgConn, false), - TestDataService.createConnectionTable("pgTable2", TestDataService.pgConn, false) - ]; + private static pgConnSchemaJson = { + "connectionName": "pgConn", + "name": "restaurants", + "type": "collection", + "queryable": true, + "children": [ + { + "connectionName": "pgConn", + "name": "grades", + "type": "embedded", + "queryable": true, + "children": [] + }, + { + "connectionName": "pgConn", + "name": "location", + "type": "embedded", + "queryable": true, + "children": [] + } + ] + }; - private static conn1ConnectionTables = [ - TestDataService.createConnectionTable("conn1Table1", TestDataService.conn1, false), - TestDataService.createConnectionTable("conn1Table2", TestDataService.conn1, false) - ]; + private static conn1SchemaJson = { + "connectionName": "conn1", + "name": "public", + "type": "schema", + "queryable": false, + "children": [ + { + "connectionName": "conn1", + "name": "customer", + "type": "table", + "queryable": true, + "children": [] + }, + { + "connectionName": "conn1", + "name": "stuff", + "type": "table", + "queryable": true, + "children": [] + } + ] + }; - private static conn2ConnectionTables = [ - TestDataService.createConnectionTable("conn2Table1", TestDataService.conn2, false), - TestDataService.createConnectionTable("conn2Table2", TestDataService.conn2, false), - TestDataService.createConnectionTable("conn2Table3", TestDataService.conn2, false), - ]; + private static conn2SchemaJson = { + "connectionName": "conn2", + "name": "restaurants", + "type": "collection", + "queryable": true, + "children": [ + { + "connectionName": "conn2", + "name": "grades", + "type": "embedded", + "queryable": true, + "children": [] + }, + { + "connectionName": "conn2", + "name": "location", + "type": "embedded", + "queryable": true, + "children": [] + } + ] + }; - private static conn3ConnectionTables = [ - TestDataService.createConnectionTable("conn3Table1", TestDataService.conn3, false), - TestDataService.createConnectionTable("conn3Table2", TestDataService.conn3, false), - TestDataService.createConnectionTable("conn3Table3", TestDataService.conn3, false), - TestDataService.createConnectionTable("conn3Table4", TestDataService.conn3, false) - ]; + private static conn3SchemaJson = { + "connectionName": "conn3", + "name": "public", + "type": "schema", + "queryable": false, + "children": [ + { + "connectionName": "conn3", + "name": "customer", + "type": "table", + "queryable": true, + "children": [] + }, + { + "connectionName": "conn3", + "name": "stuff", + "type": "table", + "queryable": true, + "children": [] + } + ] + }; // ================================================================= // Result sets @@ -710,14 +778,12 @@ export class TestDataService { "serviceVdbVersion": TestDataService.accountsVdb.getVersion(), "serviceViewModel": "views", "serviceViews": [ - "AcctView1", - "AcctView2" + "tbl1View", + "tbl2View" ], "serviceViewTables": [ - TestDataService.conn1.getId() + "BtlSource.AcctView1Table1", - TestDataService.conn1.getId() + "BtlSource.AcctView1Table2", - TestDataService.conn1.getId() + "BtlSource.AcctView1Table3", - TestDataService.conn1.getId() + "BtlSource.AcctView2Table1" + TestDataService.conn1.getId().toLowerCase() + "schemavdb.tbl1", + TestDataService.conn1.getId().toLowerCase() + "schemavdb.tbl1" ], "connections": 0, "drivers": 0, @@ -758,22 +824,16 @@ export class TestDataService { "serviceVdbVersion": TestDataService.employeesVdb.getVersion(), "serviceViewModel": "views", "serviceViews": [ - "EmpView1", - "EmpView2", - "EmpView3", - "EmpView4" + "tbl1View", + "tbl2View", + "tbl3View", + "tbl4View" ], "serviceViewTables": [ - TestDataService.conn2.getId() + "BtlSource.EmpView1Table1", - TestDataService.conn2.getId() + "BtlSource.EmpView2Table1", - TestDataService.conn2.getId() + "BtlSource.EmpView2Table2", - TestDataService.conn2.getId() + "BtlSource.EmpView3Table1", - TestDataService.conn2.getId() + "BtlSource.EmpView3Table2", - TestDataService.conn2.getId() + "BtlSource.EmpView3Table3", - TestDataService.conn2.getId() + "BtlSource.EmpView4Table1", - TestDataService.conn2.getId() + "BtlSource.EmpView4Table2", - TestDataService.conn2.getId() + "BtlSource.EmpView4Table3", - TestDataService.conn2.getId() + "BtlSource.EmpView4Table4" + TestDataService.conn2.getId().toLowerCase() + "schemavdb.tbl1", + TestDataService.conn2.getId().toLowerCase() + "schemavdb.tbl2", + TestDataService.conn2.getId().toLowerCase() + "schemavdb.tbl3", + TestDataService.conn2.getId().toLowerCase() + "schemavdb.tbl4" ], "connections": 0, "drivers": 0, @@ -814,35 +874,20 @@ export class TestDataService { "serviceVdbVersion": TestDataService.productsVdb.getVersion(), "serviceViewModel": "views", "serviceViews": [ - "ProdView1", - "ProdView2", - "ProdView3", - "ProdView4", - "ProdView5", - "ProdView6" + "tbl1View", + "tbl2View", + "tbl3View", + "tbl4View", + "tbl5View", + "tbl6View" ], "serviceViewTables": [ - TestDataService.conn3.getId() + "BtlSource.ProdView1Table1", - TestDataService.conn3.getId() + "BtlSource.ProdView2Table1", - TestDataService.conn3.getId() + "BtlSource.ProdView2Table2", - TestDataService.conn3.getId() + "BtlSource.ProdView3Table1", - TestDataService.conn3.getId() + "BtlSource.ProdView3Table2", - TestDataService.conn3.getId() + "BtlSource.ProdView3Table3", - TestDataService.conn3.getId() + "BtlSource.ProdView4Table1", - TestDataService.conn3.getId() + "BtlSource.ProdView4Table2", - TestDataService.conn3.getId() + "BtlSource.ProdView4Table3", - TestDataService.conn3.getId() + "BtlSource.ProdView4Table4", - TestDataService.conn3.getId() + "BtlSource.ProdView5Table1", - TestDataService.conn3.getId() + "BtlSource.ProdView5Table2", - TestDataService.conn3.getId() + "BtlSource.ProdView5Table3", - TestDataService.conn3.getId() + "BtlSource.ProdView5Table4", - TestDataService.conn3.getId() + "BtlSource.ProdView5Table5", - TestDataService.conn3.getId() + "BtlSource.ProdView6Table1", - TestDataService.conn3.getId() + "BtlSource.ProdView6Table2", - TestDataService.conn3.getId() + "BtlSource.ProdView6Table3", - TestDataService.conn3.getId() + "BtlSource.ProdView6Table4", - TestDataService.conn3.getId() + "BtlSource.ProdView6Table5", - TestDataService.conn3.getId() + "BtlSource.ProdView6Table6" + TestDataService.conn3.getId().toLowerCase() + "schemavdb.tbl1", + TestDataService.conn3.getId().toLowerCase() + "schemavdb.tbl2", + TestDataService.conn3.getId().toLowerCase() + "schemavdb.tbl3", + TestDataService.conn3.getId().toLowerCase() + "schemavdb.tbl4", + TestDataService.conn3.getId().toLowerCase() + "schemavdb.tbl5", + TestDataService.conn3.getId().toLowerCase() + "schemavdb.tbl6" ], "connections": 0, "drivers": 0, @@ -940,7 +985,6 @@ export class TestDataService { TestDataService.productsVdb ]; - private connectionTableMap = new Map(); private readonly vdbStatuses: VdbStatus[]; private readonly virtualizations: Virtualization[]; @@ -974,21 +1018,6 @@ export class TestDataService { return connectionSummary; } - /** - * Create a ConnectionTable using the specified info - * @param {string} name the connection table name - * @param {Connection} connection the connection - * @param {boolean} selected 'true' if the table is selected - * @returns {ConnectionSummary} - */ - private static createConnectionTable( name: string, connection: Connection, selected: boolean ): ConnectionTable { - const connectionTable = new ConnectionTable(); - connectionTable.setId(name); - connectionTable.setConnection(connection); - connectionTable.selected = selected; - return connectionTable; - } - constructor() { this.vdbStatuses = TestDataService.vdbStatuses.vdbs.map(( vdbStatus ) => VdbStatus.create( vdbStatus ) ); this.virtualizations = [ @@ -1037,15 +1066,32 @@ export class TestDataService { } /** - * @returns {Map} the array of test Service Catalog datasources + * @returns {Map} the array of SchemaNodes for connection */ - public getConnectionTableMap(): Map { - const tableMap = new Map(); - tableMap.set( TestDataService.pgConn.getId(), TestDataService.pgConnConnectionTables ); - tableMap.set( TestDataService.conn1.getId(), TestDataService.conn1ConnectionTables ); - tableMap.set( TestDataService.conn2.getId(), TestDataService.conn2ConnectionTables ); - tableMap.set( TestDataService.conn3.getId(), TestDataService.conn3ConnectionTables ); - return tableMap; + public getConnectionSchemaMap(): Map { + const nodesMap = new Map(); + + const pgConnSchemaRoots: SchemaNode[] = []; + const sNode = SchemaNode.create(TestDataService.pgConnSchemaJson); + pgConnSchemaRoots.push(sNode); + + const conn1SchemaRoots: SchemaNode[] = []; + const sNode1 = SchemaNode.create(TestDataService.conn1SchemaJson); + conn1SchemaRoots.push(sNode1); + + const conn2SchemaRoots: SchemaNode[] = []; + const sNode2 = SchemaNode.create(TestDataService.conn2SchemaJson); + conn2SchemaRoots.push(sNode2); + + const conn3SchemaRoots: SchemaNode[] = []; + const sNode3 = SchemaNode.create(TestDataService.conn3SchemaJson); + conn3SchemaRoots.push(sNode3); + + nodesMap.set( TestDataService.pgConn.getId(), pgConnSchemaRoots ); + nodesMap.set( TestDataService.conn1.getId(), conn1SchemaRoots ); + nodesMap.set( TestDataService.conn2.getId(), conn2SchemaRoots ); + nodesMap.set( TestDataService.conn3.getId(), conn3SchemaRoots ); + return nodesMap; } /** diff --git a/ngapp/src/styles.css b/ngapp/src/styles.css index 9e4a69e3..6d5e4f1e 100644 --- a/ngapp/src/styles.css +++ b/ngapp/src/styles.css @@ -1,5 +1,6 @@ /* You can add global styles to this file, and also import other style files */ +@import "~angular-tree-component/dist/angular-tree-component.css"; @import "~codemirror/lib/codemirror.css"; @import "~codemirror/theme/mdn-like.css"; @import "~patternfly/dist/css/patternfly.css"; @@ -320,3 +321,47 @@ height: 75px; padding: 0; } + +/**************************************************************/ +/* Schema Tree Types */ +/**************************************************************/ +.object-tree .node-content-wrapper, .tree-children { + position: relative; +} + +.object-tree .node-content-wrapper::before, .tree-children::after { + content: ""; + position: absolute; +} + +.object-tree .node-content-wrapper::before { + border-bottom: 1px solid lightgrey; + border-left: 1px solid lightgrey; + height: 28px; + top: -17px; + width: 20px; + left: -28px; +} + +.object-tree .tree-node-level-1 > tree-node-wrapper > .node-wrapper > .node-content-wrapper::before { + display: none; +} + +.object-tree .tree-node-leaf > .node-wrapper > .node-content-wrapper::before { + width: 25px; +} + +.object-tree .tree-children::after { + border-left: 1px solid lightgrey; + height: 100%; + top: -15px; + left: -15px; +} + +.object-tree .tree-node:last-child > .tree-node > .tree-children::after { + border-left: none; +} + +.object-tree .toggle-children { + z-index: 1; +}