From e2cb4bdcb36a6b562375b10f12bb75810f9680ef Mon Sep 17 00:00:00 2001 From: Mark Drilling Date: Tue, 31 Oct 2017 09:10:42 -0500 Subject: [PATCH] Addition of Dataservice Wizard --- .../add-activity-wizard.component.ts | 1 + ngapp/src/app/app-routing.module.ts | 2 + ngapp/src/app/app.module.ts | 4 + .../add-connection-wizard.component.ts | 1 + .../add-connection.component.html | 4 +- .../connections/connections.component.spec.ts | 2 +- ngapp/src/app/core/api.service.ts | 1 - .../vertical-nav/vertical-nav.component.html | 5 + .../vertical-nav/vertical-nav.component.ts | 16 +- .../add-dataservice-wizard.component.css | 5 + .../add-dataservice-wizard.component.html | 78 +++++ .../add-dataservice-wizard.component.spec.ts | 42 +++ .../add-dataservice-wizard.component.ts | 321 ++++++++++++++++++ .../add-dataservice.component.css | 0 .../add-dataservice.component.html | 18 + .../add-dataservice.component.spec.ts | 42 +++ .../add-dataservice.component.ts | 40 +++ .../dataservices-cards.component.css | 70 ++++ .../dataservices-cards.component.html | 29 ++ .../dataservices-cards.component.spec.ts | 28 ++ .../dataservices-cards.component.ts | 65 ++++ .../dataservices-list.component.css | 39 +++ .../dataservices-list.component.html | 33 ++ .../dataservices-list.component.spec.ts | 28 ++ .../dataservices-list.component.ts | 68 ++++ .../dataservices-routing.module.ts | 38 +++ .../dataservices/dataservices.component.css | 57 ++++ .../dataservices/dataservices.component.html | 107 ++++++ .../dataservices.component.spec.ts | 115 +++++++ .../dataservices/dataservices.component.ts | 252 ++++++++++++++ .../app/dataservices/dataservices.module.ts | 59 ++++ .../dataservices/shared/dataservice.model.ts | 89 +++++ .../shared/dataservice.service.spec.ts | 18 + .../shared/dataservice.service.ts | 80 +++++ .../shared/dataservices-constants.ts | 20 ++ .../shared/mock-dataservice.service.ts | 55 +++ .../shared/new-dataservice.model.ts | 60 ++++ ngapp/tsconfig.json | 1 + 38 files changed, 1887 insertions(+), 6 deletions(-) create mode 100644 ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.css create mode 100644 ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.html create mode 100644 ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.spec.ts create mode 100644 ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.ts create mode 100644 ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.css create mode 100644 ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.html create mode 100644 ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.spec.ts create mode 100644 ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.ts create mode 100644 ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.css create mode 100644 ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.html create mode 100644 ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.spec.ts create mode 100644 ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.ts create mode 100644 ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.css create mode 100644 ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.html create mode 100644 ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.spec.ts create mode 100644 ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.ts create mode 100644 ngapp/src/app/dataservices/dataservices-routing.module.ts create mode 100644 ngapp/src/app/dataservices/dataservices.component.css create mode 100644 ngapp/src/app/dataservices/dataservices.component.html create mode 100644 ngapp/src/app/dataservices/dataservices.component.spec.ts create mode 100644 ngapp/src/app/dataservices/dataservices.component.ts create mode 100644 ngapp/src/app/dataservices/dataservices.module.ts create mode 100644 ngapp/src/app/dataservices/shared/dataservice.model.ts create mode 100644 ngapp/src/app/dataservices/shared/dataservice.service.spec.ts create mode 100644 ngapp/src/app/dataservices/shared/dataservice.service.ts create mode 100644 ngapp/src/app/dataservices/shared/dataservices-constants.ts create mode 100644 ngapp/src/app/dataservices/shared/mock-dataservice.service.ts create mode 100644 ngapp/src/app/dataservices/shared/new-dataservice.model.ts diff --git a/ngapp/src/app/activities/add-activity-wizard/add-activity-wizard.component.ts b/ngapp/src/app/activities/add-activity-wizard/add-activity-wizard.component.ts index b3cf601f..c27f3395 100644 --- a/ngapp/src/app/activities/add-activity-wizard/add-activity-wizard.component.ts +++ b/ngapp/src/app/activities/add-activity-wizard/add-activity-wizard.component.ts @@ -239,6 +239,7 @@ export class AddActivityWizardComponent implements OnInit { self.step2bConfig.nextEnabled = false; }, (error) => { + self.logger.error("[AddActivityWizardComponent] Error: %o", error); self.createComplete = true; self.createSuccessful = false; } diff --git a/ngapp/src/app/app-routing.module.ts b/ngapp/src/app/app-routing.module.ts index 40fae7ab..b49afffa 100644 --- a/ngapp/src/app/app-routing.module.ts +++ b/ngapp/src/app/app-routing.module.ts @@ -20,6 +20,7 @@ import { NgModule } from "@angular/core"; import { RouterModule } from "@angular/router"; import { Routes } from "@angular/router"; import { ConnectionsConstants } from "@connections/shared/connections-constants"; +import { DataservicesConstants } from "@dataservices/shared/dataservices-constants"; import { environment } from "@environments/environment"; import { PageNotFoundComponent } from "@shared/page-not-found/page-not-found.component"; @@ -27,6 +28,7 @@ const appRoutes: Routes = [ { path: "", redirectTo: environment.homePagePath, pathMatch: "full" }, { path: ConnectionsConstants.connectionsRootRoute, loadChildren: "@connections/connections.module#ConnectionsModule" }, { path: ActivitiesConstants.activitiesRootRoute, loadChildren: "@activities/activities.module#ActivitiesModule" }, + { path: DataservicesConstants.dataservicesRootRoute, loadChildren: "@dataservices/dataservices.module#DataservicesModule" }, { path: "**", component: PageNotFoundComponent }, // always last ]; diff --git a/ngapp/src/app/app.module.ts b/ngapp/src/app/app.module.ts index f1acc022..ffe2ca9d 100644 --- a/ngapp/src/app/app.module.ts +++ b/ngapp/src/app/app.module.ts @@ -25,6 +25,8 @@ import { AppComponent } from "@app/app.component"; import { ConnectionsRoutingModule } from "@connections/connections-routing.module"; import { ConnectionsModule } from "@connections/connections.module"; import { CoreModule } from "@core/core.module"; +import { DataservicesModule } from "@dataservices/dataservices.module"; +import { DataservicesRoutingModule } from "@dataservices/dataservices-routing.module"; import { SharedModule } from "@shared/shared.module"; @NgModule({ @@ -36,10 +38,12 @@ import { SharedModule } from "@shared/shared.module"; BrowserModule, ConnectionsModule, CoreModule, + DataservicesModule, RouterModule, SharedModule, ActivitiesRoutingModule, ConnectionsRoutingModule, + DataservicesRoutingModule, AppRoutingModule // last so its routes are check after all other routes ], providers: [], diff --git a/ngapp/src/app/connections/add-connection-wizard/add-connection-wizard.component.ts b/ngapp/src/app/connections/add-connection-wizard/add-connection-wizard.component.ts index 5b907fe5..aecaace3 100644 --- a/ngapp/src/app/connections/add-connection-wizard/add-connection-wizard.component.ts +++ b/ngapp/src/app/connections/add-connection-wizard/add-connection-wizard.component.ts @@ -285,6 +285,7 @@ export class AddConnectionWizardComponent implements OnInit { self.step3bConfig.nextEnabled = false; }, (error) => { + self.logger.error("[AddConnectionWizardComponent] Error: %o", error); self.createComplete = true; self.createSuccessful = false; } diff --git a/ngapp/src/app/connections/add-connection/add-connection.component.html b/ngapp/src/app/connections/add-connection/add-connection.component.html index 983950e3..250c1ea7 100644 --- a/ngapp/src/app/connections/add-connection/add-connection.component.html +++ b/ngapp/src/app/connections/add-connection/add-connection.component.html @@ -1,8 +1,8 @@
-
  • -
  • +
  • +
  • diff --git a/ngapp/src/app/connections/connections.component.spec.ts b/ngapp/src/app/connections/connections.component.spec.ts index 757dfb21..99eb20d0 100644 --- a/ngapp/src/app/connections/connections.component.spec.ts +++ b/ngapp/src/app/connections/connections.component.spec.ts @@ -107,7 +107,7 @@ describe("ConnectionsComponent", () => { component.filterConnections(); fixture.detectChanges(); - // Now expect 0 activities match + // Now expect 0 connections match connections = component.filteredConnections; expect(connections.length).toEqual(0); }); diff --git a/ngapp/src/app/core/api.service.ts b/ngapp/src/app/core/api.service.ts index 293020e8..0e95798c 100644 --- a/ngapp/src/app/core/api.service.ts +++ b/ngapp/src/app/core/api.service.ts @@ -15,7 +15,6 @@ * limitations under the License. */ -import { Injectable } from "@angular/core"; import { Headers, RequestOptions, Response } from "@angular/http"; import { LoggerService } from "@core/logger.service"; import "rxjs/add/observable/throw"; diff --git a/ngapp/src/app/core/vertical-nav/vertical-nav.component.html b/ngapp/src/app/core/vertical-nav/vertical-nav.component.html index e20bf9da..80963454 100644 --- a/ngapp/src/app/core/vertical-nav/vertical-nav.component.html +++ b/ngapp/src/app/core/vertical-nav/vertical-nav.component.html @@ -6,6 +6,11 @@
    Activities
    +
    + +
    DataServices
    +
    diff --git a/ngapp/src/app/core/vertical-nav/vertical-nav.component.ts b/ngapp/src/app/core/vertical-nav/vertical-nav.component.ts index fe3094d6..275511d7 100644 --- a/ngapp/src/app/core/vertical-nav/vertical-nav.component.ts +++ b/ngapp/src/app/core/vertical-nav/vertical-nav.component.ts @@ -19,13 +19,14 @@ import { ActivitiesConstants } from "@activities/shared/activities-constants"; import { Component, OnInit } from "@angular/core"; import { NavigationEnd, Router } from "@angular/router"; import { ConnectionsConstants } from "@connections/shared/connections-constants"; +import { DataservicesConstants } from "@dataservices/shared/dataservices-constants"; import { LoggerService } from "@core/logger.service"; /** * Models the menus off the main left-hand vertical nav. */ enum VerticalNavType { - Home, Activities, Connections + Home, Activities, Connections, Dataservices } @Component({ @@ -79,7 +80,18 @@ export class VerticalNavComponent implements OnInit { }); } - /** + /** + * Called when the user clicks the vertical menu Dataservices item. + */ + public onDataservicesClick(): void { + this.currentMenu = VerticalNavType.Dataservices; + const link: string[] = [ DataservicesConstants.dataservicesRootPath ]; + this.router.navigate(link).then(() => { + // nothing to do + }); + } + + /** * Called when the user clicks the vertical menu shade (the grey shaded area behind the submenu div that * is displayed when a sub-menu is selected). Clicking the shade makes the sub-menu div go away. */ diff --git a/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.css b/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.css new file mode 100644 index 00000000..0f0ecbda --- /dev/null +++ b/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.css @@ -0,0 +1,5 @@ +.wizard-pf-failed-icon { + color: #9c3535; + font-size: 67px; + line-height: 67px; +} 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 new file mode 100644 index 00000000..51c22fa3 --- /dev/null +++ b/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.html @@ -0,0 +1,78 @@ + + + + + +

    {{ step1InstructionMessage }}

    +
    +
    +
    +
    +
    + +
    + +
    {{ getBasicPropertyErrorMessage("name") }}
    +
    +
    +
    + +
    + +
    {{ getBasicPropertyErrorMessage("connection") }}
    +
    +
    +
    +
    + + + + + + +

    {{ step2InstructionMessage }}

    +

    Dataservice Properties:

    +
    +
    + + +
    +
    + + +
    +
    +
    + + +
    + +
    +
    +

    Creation in progress

    +

    The dataservice is being created.

    +
    + +
    +
    +

    Creation was successful

    +

    The dataservice was created successfully. Click on the button to see all dataservices.

    + View All Dataservices +
    + +
    +
    +

    Creation failed

    +

    The dataservice creation failed. Correct any properties and retry.

    +
    +
    +
    +
    +
    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 new file mode 100644 index 00000000..be3c7a2d --- /dev/null +++ b/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.spec.ts @@ -0,0 +1,42 @@ +import { async, ComponentFixture, TestBed } from "@angular/core/testing"; + +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { RouterTestingModule } from "@angular/router/testing"; +import { DataserviceService } from "@dataservices/shared/dataservice.service"; +import { MockDataserviceService } from "@dataservices/shared/mock-dataservice.service"; +import { CoreModule } from "@core/core.module"; +import { PropertyFormPropertyComponent } from "@shared/property-form/property-form-property/property-form-property.component"; +import { PropertyFormComponent } from "@shared/property-form/property-form.component"; +import { PatternFlyNgModule } from "patternfly-ng"; +import { AddDataserviceWizardComponent } from "./add-dataservice-wizard.component"; +import { ConnectionService } from "@connections/shared/connection.service"; +import { MockConnectionService } from "@connections/shared/mock-connection.service"; + +describe("AddDataserviceWizardComponent", () => { + let component: AddDataserviceWizardComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ CoreModule, FormsModule, PatternFlyNgModule, ReactiveFormsModule, RouterTestingModule ], + declarations: [ AddDataserviceWizardComponent, PropertyFormComponent, PropertyFormPropertyComponent ], + providers: [ + { provide: DataserviceService, useClass: MockDataserviceService }, + { provide: ConnectionService, useClass: MockConnectionService }, + ] + }) + .compileComponents().then(() => { + // nothing to do + }); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AddDataserviceWizardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should be created", () => { + expect(component).toBeTruthy(); + }); +}); 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 new file mode 100644 index 00000000..cc347b06 --- /dev/null +++ b/ngapp/src/app/dataservices/add-dataservice-wizard/add-dataservice-wizard.component.ts @@ -0,0 +1,321 @@ +/** + * @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, + ViewChild, + ViewEncapsulation, +} from "@angular/core"; + +import { FormControl, FormGroup } from "@angular/forms"; +import { AbstractControl } from "@angular/forms"; +import { Validators } from "@angular/forms"; +import { Router } from "@angular/router"; +import { Connection } from "@connections/shared/connection.model"; +import { ConnectionService } from "@connections/shared/connection.service"; +import { DataserviceService } from "@dataservices/shared/dataservice.service"; +import { DataservicesConstants } from "@dataservices/shared/dataservices-constants"; +import { NewDataservice } from "@dataservices/shared/new-dataservice.model"; +import { LoggerService } from "@core/logger.service"; +import { WizardComponent } from "patternfly-ng"; +import { WizardEvent } from "patternfly-ng"; +import { WizardStepConfig } from "patternfly-ng"; +import { WizardConfig } from "patternfly-ng"; + +@Component({ + encapsulation: ViewEncapsulation.None, + selector: "app-add-dataservice-wizard", + templateUrl: "./add-dataservice-wizard.component.html" +}) +export class AddDataserviceWizardComponent implements OnInit { + public readonly dataserviceSummaryLink: string = DataservicesConstants.dataservicesRootPath; + + // Wizard Config + public wizardConfig: WizardConfig; + + public basicPropertyForm: FormGroup; + public createComplete = true; + public createSuccessful = false; + public connectionsLoaded = false; + + // Wizard Step 1 + public step1Config: WizardStepConfig; + + // Wizard Step 2 + public step2Config: WizardStepConfig; + public step2aConfig: WizardStepConfig; + public step2bConfig: WizardStepConfig; + + @ViewChild("wizard") public wizard: WizardComponent; + + private connectionService: ConnectionService; + private dataserviceService: DataserviceService; + private allConnections: Connection[] = []; + private logger: LoggerService; + private router: Router; + + constructor( router: Router, connectionService: ConnectionService, dataserviceService: DataserviceService, logger: LoggerService ) { + this.connectionService = connectionService; + this.dataserviceService = dataserviceService; + this.router = router; + this.logger = logger; + this.createBasicPropertyForm(); + } + + /* + * Initialization + */ + public ngOnInit(): void { + // Step 1 - Basic Properties + this.step1Config = { + id: "step1", + priority: 0, + title: "Basic Properties", + allowClickNav: false + } as WizardStepConfig; + + // Step 2 - Review and Create + this.step2Config = { + id: "step2", + priority: 0, + title: "Review and Create", + allowClickNav: false + } as WizardStepConfig; + this.step2aConfig = { + id: "step2a", + priority: 0, + title: "Review", + allowClickNav: false + } as WizardStepConfig; + this.step2bConfig = { + id: "step2b", + priority: 1, + title: "Create", + allowClickNav: false + } as WizardStepConfig; + + // Wizard Configuration + this.wizardConfig = { + embedInPage: true, + loadingTitle: "Add Dataservice Wizard loading", + loadingSecondaryInfo: "Please wait for the wizard to finish loading...", + title: "Add Dataservice", + contentHeight: "500px" + } as WizardConfig; + + // Load the connections for the first step + this.connectionsLoaded = false; + const self = this; + this.connectionService + .getAllConnections() + .subscribe( + (conns) => { + self.allConnections = conns; + self.connectionsLoaded = true; + }, + (error) => { + self.logger.error("[AddDataserviceWizardComponent] Error getting connections: %o", error); + } + ); + + this.setNavAway(false); + } + + // ---------------- + // Public Methods + // ---------------- + /* + * Return the name valid state + */ + public get nameValid(): boolean { + return this.basicPropertyForm.controls["name"].valid; + } + + /* + * Return the connection valid state + */ + public get connectionValid(): boolean { + return this.basicPropertyForm.controls["connection"].valid; + } + + /* + * Step 1 instruction message + */ + public get step1InstructionMessage(): string { + if (!this.nameValid) { + return "Please enter a name for the Dataservice"; + } else if (!this.connectionValid) { + return "Please choose a connection for the Dataservice"; + } else { + return "When finished entering properties, click Next to continue"; + } + } + + /* + * Step 2 instruction message + */ + public get step2InstructionMessage(): string { + return "Review your entries. When finished, click Create to create the Dataservice"; + } + + /* + * Return the name error message if invalid + */ + public getBasicPropertyErrorMessage( name: string ): string { + const control: AbstractControl = this.basicPropertyForm.controls[name]; + if (control.invalid) { + // The first error found is returned + if (control.errors.required) { + return name + " is a required property"; + } + } + return ""; + } + + public nextClicked($event: WizardEvent): void { + // When leaving page 1, load the driver-specific property definitions + if ($event.step.config.id === "step1") { + } + } + + public cancelClicked($event: WizardEvent): void { + const link: string[] = [ DataservicesConstants.dataservicesRootPath ]; + this.logger.log("[AddDataserviceWizardComponent] Navigating to: %o", link); + this.router.navigate(link).then(() => { + // nothing to do + }); + } + + /* + * Create the Dataservice via komodo REST interface, + * using the currently entered properties + */ + public createDataservice(): void { + this.createComplete = false; + this.wizardConfig.done = true; + + const dataservice: NewDataservice = new NewDataservice(); + + // Dataservice basic properties from step 1 + dataservice.setId(this.dataserviceName); + + const self = this; + this.dataserviceService + .createDataservice(dataservice) + .subscribe( + () => { + self.createComplete = true; + self.createSuccessful = true; + self.step2bConfig.nextEnabled = false; + }, + (error) => { + self.logger.error("[AddDataserviceWizardComponent] Error: %o", error); + self.createComplete = true; + self.createSuccessful = false; + } + ); + } + + public stepChanged($event: WizardEvent): void { + if ($event.step.config.id === "step1") { + this.updatePage1ValidStatus(); + this.wizardConfig.nextTitle = "Next >"; + } else if ($event.step.config.id === "step2a") { + this.wizardConfig.nextTitle = "Create"; + } else if ($event.step.config.id === "step2b") { + // Note: The next button is not disabled by default when wizard is done + this.step2Config.nextEnabled = false; + } else { + this.wizardConfig.nextTitle = "Next >"; + } + } + + /** + * Handler for property form initialization + * @param {boolean} isValid form valid state + */ + public onDetailPropertyInit(isValid: boolean): void { + this.updatePage2ValidStatus(isValid); + } + + /** + * Handler for property form changes + * @param {boolean} isValid form valid state + */ + public onDetailPropertyChanged(isValid: boolean): void { + this.updatePage2ValidStatus(isValid); + } + + /** + * @returns {string} the name of the dataservice + */ + public get dataserviceName(): string { + return this.basicPropertyForm.controls["name"].value; + } + + /** + * @returns {string} the connection name of the dataservice + */ + public get connectionName(): string { + return this.basicPropertyForm.controls["connection"].value; + } + + /* + * Return the array of connection names + */ + public get connectionNames(): string[] { + const connNames: string[] = []; + for ( const conn of this.allConnections ) { + connNames.push(conn.getId()); + } + return connNames.sort(); + } + + // ---------------- + // Private Methods + // ---------------- + + /* + * Create the BasicProperty form (page 1) + */ + private createBasicPropertyForm(): void { + this.basicPropertyForm = new FormGroup({ + name: new FormControl("", Validators.required), + connection: new FormControl("", Validators.required) + }); + // Responds to basic property changes - updates the page status + this.basicPropertyForm.valueChanges.subscribe((val) => { + this.updatePage1ValidStatus( ); + }); + } + + private setNavAway(allow: boolean): void { + this.step1Config.allowNavAway = allow; + } + + private updatePage1ValidStatus( ): void { + this.step1Config.nextEnabled = this.basicPropertyForm.valid; + this.setNavAway(this.step1Config.nextEnabled); + } + + private updatePage2ValidStatus(formValid: boolean): void { + this.step2Config.nextEnabled = formValid; + this.setNavAway(this.step2Config.nextEnabled); + } + +} diff --git a/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.css b/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.css new file mode 100644 index 00000000..e69de29b diff --git a/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.html b/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.html new file mode 100644 index 00000000..be280356 --- /dev/null +++ b/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.html @@ -0,0 +1,18 @@ +
    +
    + +
  • +
  • +
    +
    +
    +
    +

    +
    + +
    + +
    +
    +
    + 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 new file mode 100644 index 00000000..eff5b9f8 --- /dev/null +++ b/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.spec.ts @@ -0,0 +1,42 @@ +import { async, ComponentFixture, TestBed } from "@angular/core/testing"; + +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { RouterTestingModule } from "@angular/router/testing"; +import { AddDataserviceWizardComponent } from "@dataservices/add-dataservice-wizard/add-dataservice-wizard.component"; +import { CoreModule } from "@core/core.module"; +import { SharedModule } from "@shared/shared.module"; +import { PatternFlyNgModule } from "patternfly-ng"; +import { AddDataserviceComponent } from "./add-dataservice.component"; +import { DataserviceService } from "@dataservices/shared/dataservice.service"; +import { MockDataserviceService } from "@dataservices/shared/mock-dataservice.service"; +import { ConnectionService } from "@connections/shared/connection.service"; +import { MockConnectionService } from "@connections/shared/mock-connection.service"; + +describe("AddDataserviceComponent", () => { + let component: AddDataserviceComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ CoreModule, PatternFlyNgModule, FormsModule, ReactiveFormsModule, RouterTestingModule, SharedModule ], + declarations: [ AddDataserviceComponent, AddDataserviceWizardComponent ], + providers: [ + { provide: DataserviceService, useClass: MockDataserviceService }, + { provide: ConnectionService, useClass: MockConnectionService } + ] + }) + .compileComponents().then(() => { + // nothing to do + }); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AddDataserviceComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should be created", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.ts b/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.ts new file mode 100644 index 00000000..d5b96f24 --- /dev/null +++ b/ngapp/src/app/dataservices/add-dataservice/add-dataservice.component.ts @@ -0,0 +1,40 @@ +/** + * @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 } from "@angular/core"; +import { DataservicesConstants } from "@dataservices/shared/dataservices-constants"; + +@Component({ + selector: "app-add-dataservice-page", + templateUrl: "./add-dataservice.component.html", + styleUrls: ["./add-dataservice.component.css"] +}) +export class AddDataserviceComponent implements OnInit { + + public readonly dataservicesLink = DataservicesConstants.dataservicesRootPath; + + public pageError: any = ""; + + constructor() { + // Nothing + } + + public ngOnInit(): void { + // Nothing + } + +} diff --git a/ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.css b/ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.css new file mode 100644 index 00000000..64e6b57d --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.css @@ -0,0 +1,70 @@ +.container-cards-pf { + padding: 0; + margin-top: 0; +} + +.row-cards-pf { + padding: 0; +} + +.dataservice-card-action-icon { + cursor: pointer; +} + +.dataservice-card-icon { + border: 2px solid #39a5dc; + border-radius: 50%; + padding: 5px; + margin-right: 10px; + width: 32px; +} + +.dataservice-card .dataservice-card-title { + font-size: 16px; + font-weight: bold; +} + +.dataservice-card { + -webkit-transition: background-color 300ms; + -moz-transition: background-color 300ms; + //-ms-transition: background-color 300ms; + -o-transition: background-color 300ms; + transition: background-color 300ms; + height: 160px; +} +.dataservice-card:hover { + background-color: rgb(237, 237, 237); +} + +.dataservice-card.active { + background-color: rgb(221, 234, 255); +} + +/* +.dataservice-description { + font-size: 13px; + overflow-y: auto; +} +*/ + +.dataservice-card .dataservice-tags { + margin-bottom: 8px; +} +.dataservice-card .dataservice-tags .dataservice-tags-label { + font-weight: bold; + margin-right: 5px; +} +.dataservice-card .dataservice-tags /*.dataservice-tag*/ { + margin-right: 5px; + border: 1px solid #ccc; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + padding: 2px 4px; +} +.dataservice-card .dataservice-tags /*.dataservice-tag:hover*/ { + cursor: pointer; + background-color: #0088ce; + border-color: #00659c; + color: white; +} diff --git a/ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.html b/ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.html new file mode 100644 index 00000000..98f74c50 --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.html @@ -0,0 +1,29 @@ +
    +
    + +
    +
    +
    + +

    + + {{ dataservice.getId() }} + +

    +
    + +
    +
    +
    + +
    +
    diff --git a/ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.spec.ts b/ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.spec.ts new file mode 100644 index 00000000..c701242b --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.spec.ts @@ -0,0 +1,28 @@ +import { async, ComponentFixture, TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { DataservicesCardsComponent } from "@dataservices/dataservices-cards/dataservices-cards.component"; + +describe("DataservicesCardsComponent", () => { + let component: DataservicesCardsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ RouterTestingModule ], + declarations: [ DataservicesCardsComponent ] + }) + .compileComponents().then(() => { + // nothing to do + }); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DataservicesCardsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should be created", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.ts b/ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.ts new file mode 100644 index 00000000..c3aa26d6 --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices-cards/dataservices-cards.component.ts @@ -0,0 +1,65 @@ +/** + * @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, Input, Output } from "@angular/core"; +import { Dataservice } from "@dataservices/shared/dataservice.model"; + +@Component({ + moduleId: module.id, + selector: "app-dataservices-cards", + templateUrl: "dataservices-cards.component.html", + styleUrls: ["dataservices-cards.component.css"] +}) +export class DataservicesCardsComponent { + + @Input() public dataservices: Dataservice[]; + @Input() public selectedDataservices: Dataservice[]; + @Output() public dataserviceSelected: EventEmitter = new EventEmitter(); + @Output() public dataserviceDeselected: EventEmitter = new EventEmitter(); + @Output() public tagSelected: EventEmitter = new EventEmitter(); + @Output() public deleteDataservice: EventEmitter = new EventEmitter(); + + /** + * Constructor. + */ + constructor() { + // nothing to do + } + + public toggleDataserviceSelected(dataservice: Dataservice): void { + if (this.isSelected(dataservice)) { + this.dataserviceDeselected.emit(dataservice); + } else { + this.dataserviceSelected.emit(dataservice); + } + } + + public isSelected(dataservice: Dataservice): boolean { + return this.selectedDataservices.indexOf(dataservice) !== -1; + } + + public onDeleteDataservice(dataserviceName: string): void { + this.deleteDataservice.emit(dataserviceName); + } + + public onSelectTag(tag: string, event: MouseEvent): void { + event.stopPropagation(); + event.preventDefault(); + this.tagSelected.emit(tag); + } + +} diff --git a/ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.css b/ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.css new file mode 100644 index 00000000..e84a38a3 --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.css @@ -0,0 +1,39 @@ +.list-view-pf-main-info { + padding-bottom: 5px; + padding-top: 5px; +} +.list-group-item { + -webkit-transition: background-color 300ms; + -moz-transition: background-color 300ms; + //-ms-transition: background-color 300ms; + -o-transition: background-color 300ms; + transition: background-color 300ms; +} +.list-group-item, .list-group-item:first-child { + margin-bottom: 5px; + border-top: 1px solid rgb(57, 165, 220); +} +.list-group-item.active { + background-color: #ffffff; +} + +.list-group-item .dataservice-tags { +} +.list-group-item .dataservice-tags .dataservice-tags-label { + font-weight: bold; + margin-right: 5px; +} +.list-group-item .dataservice-tags /*.dataservice-tag*/ { + margin-right: 5px; + border: 1px solid #ccc; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + padding: 2px 4px; +} +.list-group-item .dataservice-tags /*.dataservice-tag:hover*/ { + cursor: pointer; + background-color: #0088ce; + border-color: #00659c; + color: white; +} diff --git a/ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.html b/ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.html new file mode 100644 index 00000000..04e91f78 --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.html @@ -0,0 +1,33 @@ +
    +
    +
    +
    + +
    +
    + +
    + +
    +
    +
    + +
    +
    +
    +
    diff --git a/ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.spec.ts b/ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.spec.ts new file mode 100644 index 00000000..8a5fe78e --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.spec.ts @@ -0,0 +1,28 @@ +import { async, ComponentFixture, TestBed } from "@angular/core/testing"; +import { RouterTestingModule } from "@angular/router/testing"; +import { DataservicesListComponent } from "@dataservices/dataservices-list/dataservices-list.component"; + +describe("DataservicesListComponent", () => { + let component: DataservicesListComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ RouterTestingModule ], + declarations: [ DataservicesListComponent ] + }) + .compileComponents().then(() => { + // nothing to do + }); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DataservicesListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should be created", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.ts b/ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.ts new file mode 100644 index 00000000..699d7055 --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices-list/dataservices-list.component.ts @@ -0,0 +1,68 @@ +/** + * @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, Input, Output } from "@angular/core"; +import { Router } from "@angular/router"; +import { Dataservice } from "@dataservices/shared/dataservice.model"; + +@Component({ + moduleId: module.id, + selector: "app-dataservices-list", + templateUrl: "dataservices-list.component.html", + styleUrls: ["dataservices-list.component.css"] +}) +export class DataservicesListComponent { + + @Input() public dataservices: Dataservice[]; + @Input() public selectedDataservices: Dataservice[]; + @Output() public dataserviceSelected: EventEmitter = new EventEmitter(); + @Output() public dataserviceDeselected: EventEmitter = new EventEmitter(); + @Output() public tagSelected: EventEmitter = new EventEmitter(); + @Output() public deleteDataservice: EventEmitter = new EventEmitter(); + + private router: Router; + + /** + * Constructor. + */ + constructor(router: Router) { + this.router = router; + } + + public toggleDataserviceSelected(dataservice: Dataservice): void { + if (this.isSelected(dataservice)) { + this.dataserviceDeselected.emit(dataservice); + } else { + this.dataserviceSelected.emit(dataservice); + } + } + + public isSelected(dataservice: Dataservice): boolean { + return this.selectedDataservices.indexOf(dataservice) !== -1; + } + + public onDeleteDataservice(dataserviceName: string): void { + this.deleteDataservice.emit(dataserviceName); + } + + public onSelectTag(tag: string, event: MouseEvent): void { + event.stopPropagation(); + event.preventDefault(); + this.tagSelected.emit(tag); + } + +} diff --git a/ngapp/src/app/dataservices/dataservices-routing.module.ts b/ngapp/src/app/dataservices/dataservices-routing.module.ts new file mode 100644 index 00000000..7494aff7 --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices-routing.module.ts @@ -0,0 +1,38 @@ +/** + * @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 { NgModule } from "@angular/core"; +import { RouterModule } from "@angular/router"; +import { Routes } from "@angular/router"; +import { AddDataserviceComponent } from "@dataservices/add-dataservice/add-dataservice.component"; +import { DataservicesComponent } from "@dataservices/dataservices.component"; +import { DataservicesConstants } from "@dataservices/shared/dataservices-constants"; + +const dataservicesRoutes: Routes = [ + { path: DataservicesConstants.dataservicesRootRoute, component: DataservicesComponent }, + { path: DataservicesConstants.addDataserviceRoute, component: AddDataserviceComponent } +]; + +@NgModule({ + imports: [ + RouterModule.forChild( dataservicesRoutes ) + ], + exports: [ + RouterModule + ] +}) +export class DataservicesRoutingModule {} diff --git a/ngapp/src/app/dataservices/dataservices.component.css b/ngapp/src/app/dataservices/dataservices.component.css new file mode 100644 index 00000000..c3505e40 --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices.component.css @@ -0,0 +1,57 @@ +.toolbar-pf { + margin-top: 5px; +} + +.toolbar-pf form { + margin-bottom: 0; +} + +.toolbar-pf-results { + margin-top: 0; +} + +.dataservice-list-items { + margin-top: 10px +} + +.dataservice-list-dataservices .toolbar-pf { + background-color: transparent; +} + +.dataservice-list-dataservices .toolbar-pf .toolbar-pf-results { + background-color: white; +} + +a.clear-filters { + cursor: pointer; +} + +.toolbar-pf-view-selector { + float: right; +} + +.toolbar-pf-view-selector li a { + cursor: pointer; + color: #333; +} + +.toolbar-pf-view-selector li a:hover { + color: #0088ce; +} + +.dataservice-list-items .empty-state { + text-align: center; +} + +.blank-slate-pf { + background-color: white; + width: 50%; + min-width: 500px; + margin-left: auto; + margin-right: auto; + margin-top: 15px; +} + +.dataservice-list-items .none-matched-state .alert { + background-color: white; +} diff --git a/ngapp/src/app/dataservices/dataservices.component.html b/ngapp/src/app/dataservices/dataservices.component.html new file mode 100644 index 00000000..7fce43ae --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices.component.html @@ -0,0 +1,107 @@ +
    + +
    + +
  • +
    +
    + + + +
    +
    +

    Dataservices

    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
      +
    • +
    • +
    +
    +
    +
    +
    +
    {{ filteredDataservices.length }} Dataservices found + (out of {{ + allDataservices.length }} total)
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +

    No Dataservices Found

    +

    + No Dataservices were found - please click below to create a new Dataservice! +

    +
    + +
    +
    +
    +
    +
    + + No Dataservices matched the filter!  Please try changing your filter criteria... +
    +
    + + +
    +
    +
    +
    +
    +

    + Loading Dataservices... +

    +
    +
    +
    +
    +
    + + +
    + + +
    + +
    + +
    + +
    + +

    Do you really want to delete the selected Dataservice?

    +
    diff --git a/ngapp/src/app/dataservices/dataservices.component.spec.ts b/ngapp/src/app/dataservices/dataservices.component.spec.ts new file mode 100644 index 00000000..5f8bbab1 --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices.component.spec.ts @@ -0,0 +1,115 @@ +import { async, ComponentFixture, TestBed } from "@angular/core/testing"; +import { FormsModule } from "@angular/forms"; +import { HttpModule } from "@angular/http"; +import { By } from "@angular/platform-browser"; +import { RouterTestingModule } from "@angular/router/testing"; +import { DataservicesCardsComponent } from "@dataservices/dataservices-cards/dataservices-cards.component"; +import { DataservicesListComponent } from "@dataservices/dataservices-list/dataservices-list.component"; +import { DataservicesComponent } from "@dataservices/dataservices.component"; +import { DataserviceService } from "@dataservices/shared/dataservice.service"; +import { MockDataserviceService } from "@dataservices/shared/mock-dataservice.service"; +import { CoreModule } from "@core/core.module"; +import { SharedModule } from "@shared/shared.module"; +import { ModalModule } from "ngx-bootstrap"; + +describe("DataservicesComponent", () => { + let component: DataservicesComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ CoreModule, FormsModule, HttpModule, ModalModule.forRoot(), RouterTestingModule, SharedModule ], + declarations: [ DataservicesComponent, DataservicesListComponent, DataservicesCardsComponent ] + }); + + // use mock service + TestBed.overrideComponent( DataservicesComponent, { + set: { + providers: [ + { provide: DataserviceService, useClass: MockDataserviceService }, + ] + } + }); + + fixture = TestBed.createComponent(DataservicesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it("should be created", () => { + expect(component).toBeTruthy(); + }); + + it("should have Dataservices Title", () => { + // query for the title

    by CSS element selector + const de = fixture.debugElement.query(By.css("h2")); + const el = de.nativeElement; + expect(el.textContent).toEqual("Dataservices"); + }); + + it("should have Toolbar", () => { + // query for the toolbar by css classname + const de = fixture.debugElement.query(By.css(".toolbar-pf")); + expect(de).toBeDefined(); + }); + + it("should have Dataservices", () => { + // Check component object + const dataservices = component.allDataservices; + expect(dataservices.length).toEqual(3); + + // Check html has the same number of dataservice cards + const cardDebugElems = fixture.debugElement.queryAll(By.css(".dataservice-card-title")); + expect(cardDebugElems).toBeDefined(); + expect(cardDebugElems.length).toEqual(3); + }); + + it("should have initial card layout", () => { + // app-dataservices-cards should be present + let debugEl = fixture.debugElement.query(By.css("app-dataservices-cards")); + const element = debugEl.nativeElement; + expect(element).toBeDefined(); + + // app-dataservices-list should not be present + debugEl = fixture.debugElement.query(By.css("app-dataservices-list")); + expect(debugEl).toBeNull(); + }); + + it("should toggle layout", () => { + // Initial layout should be Card Layout + let cardDebugElem = fixture.debugElement.query(By.css("app-dataservices-cards")); + let listDebugElem = fixture.debugElement.query(By.css("app-dataservices-list")); + expect(cardDebugElem).toBeDefined(); + expect(listDebugElem).toBeNull(); + const cardElem = cardDebugElem.nativeElement; + expect(cardElem).toBeDefined(); + + // Change the layout to ListLayout + component.setListLayout(); + fixture.detectChanges(); + + // Verify that the layout has changed + cardDebugElem = fixture.debugElement.query(By.css("app-dataservices-cards")); + listDebugElem = fixture.debugElement.query(By.css("app-dataservices-list")); + expect(cardDebugElem).toBeNull(); + expect(listDebugElem).toBeDefined(); + const listElem = listDebugElem.nativeElement; + expect(listElem).toBeDefined(); + }); + + it("should filter dataservices", () => { + // Expect 3 dataservices initially. + let dataservices = component.filteredDataservices; + expect(dataservices.length).toEqual(3); + + // Set a name filter which satisfies none of the dataservices + component.nameFilter = "g"; + component.filterDataservices(); + fixture.detectChanges(); + + // Now expect 0 services match + dataservices = component.filteredDataservices; + expect(dataservices.length).toEqual(0); + }); + +}); diff --git a/ngapp/src/app/dataservices/dataservices.component.ts b/ngapp/src/app/dataservices/dataservices.component.ts new file mode 100644 index 00000000..7a9a76d4 --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices.component.ts @@ -0,0 +1,252 @@ +/** + * @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, ViewChild } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; +import { Dataservice } from "@dataservices/shared/dataservice.model"; +import { DataserviceService } from "@dataservices/shared/dataservice.service"; +import { DataservicesConstants } from "@dataservices/shared/dataservices-constants"; +import { NewDataservice } from "@dataservices/shared/new-dataservice.model"; +import { LoggerService } from "@core/logger.service"; +import { ArrayUtils } from "@core/utils/array-utils"; +import { AbstractPageComponent } from "@shared/abstract-page.component"; +import { ConfirmDeleteComponent } from "@shared/confirm-delete/confirm-delete.component"; +import { IdFilter } from "@shared/id-filter"; +import { LayoutType } from "@shared/layout-type.enum"; +import { SortDirection } from "@shared/sort-direction.enum"; + +@Component({ + moduleId: module.id, + selector: "app-dataservices", + templateUrl: "./dataservices.component.html", + styleUrls: ["./dataservices.component.css"] +}) +export class DataservicesComponent extends AbstractPageComponent { + + public readonly addDataserviceLink: string = DataservicesConstants.addDataservicePath; + + private allServices: Dataservice[] = []; + private filteredServices: Dataservice[] = []; + private selectedServices: Dataservice[] = []; + private dataserviceNameForDelete: string; + private router: Router; + private dataserviceService: DataserviceService; + private filter: IdFilter = new IdFilter(); + private layout: LayoutType = LayoutType.CARD; + private sortDirection: SortDirection = SortDirection.ASC; + + @ViewChild(ConfirmDeleteComponent) private confirmDeleteDialog: ConfirmDeleteComponent; + + constructor(router: Router, route: ActivatedRoute, dataserviceService: DataserviceService, logger: LoggerService) { + super(route, logger); + this.router = router; + this.dataserviceService = dataserviceService; + } + + public loadAsyncPageData(): void { + const self = this; + this.dataserviceService + .getAllDataservices() + .subscribe( + (dataservices) => { + self.allServices = dataservices; + self.filteredServices = this.filterDataservices(); + self.loaded("dataservices"); + }, + (error) => { + self.logger.error("[DataservicesComponent] Error getting dataservices."); + self.error(error); + } + ); + } + + /** + * @returns {boolean} true if dataservices are being represented by cards + */ + public get isCardLayout(): boolean { + return this.layout === LayoutType.CARD; + } + + /** + * @returns {boolean} true if dataservices are being represented by items in a list + */ + public get isListLayout(): boolean { + return this.layout === LayoutType.LIST; + } + + /** + * @returns {boolean} true if sorting dataservice names in ascending order + */ + public get isSortAscending(): boolean { + return this.sortDirection === SortDirection.ASC; + } + + /** + * @returns {boolean} true if sorting dataservice names in descending order + */ + public get isSortDescending(): boolean { + return this.sortDirection === SortDirection.DESC; + } + + /** + * @returns {Dataservice[]} the array of all dataservices + */ + public get allDataservices(): Dataservice[] { + return this.allServices; + } + + /** + * @returns {Dataservice[]} the array of filtered dataservices + */ + public get filteredDataservices(): Dataservice[] { + return this.filteredServices; + } + + /** + * @returns {Dataservice[]} the array of selected dataservices + */ + public get selectedDataservices(): Dataservice[] { + return this.selectedServices; + } + + public onSelected(dataservice: Dataservice): void { + // Only allow one item to be selected + this.selectedServices.shift(); + this.selectedServices.push(dataservice); + } + + public onDeselected(dataservice: Dataservice): void { + // Only one item is selected at a time + this.selectedServices.shift(); + // this.selectedServices.splice(this.selectedServices.indexOf(dataservice), 1); + } + + public onDelete(svcName: string): void { + this.dataserviceNameForDelete = svcName; + this.confirmDeleteDialog.open(); + } + + public isFiltered(): boolean { + return this.allServices.length !== this.filteredServices.length; + } + + public get nameFilter(): string { + return this.filter.getPattern(); + } + + /** + * @param {string} pattern the new pattern for the dataservice name filter (can be null or empty) + */ + public set nameFilter( pattern: string ) { + this.filter.setFilter( pattern ); + } + + public toggleSortDirection(): void { + if (this.sortDirection === SortDirection.ASC) { + this.sortDirection = SortDirection.DESC; + } else { + this.sortDirection = SortDirection.ASC; + } + this.filterDataservices(); + } + + public clearFilters(): void { + this.filter.reset(); + this.filterDataservices(); + } + + public setListLayout(): void { + this.layout = LayoutType.LIST; + } + + public setCardLayout(): void { + this.layout = LayoutType.CARD; + } + + /** + * Called to doDelete all selected APIs. + */ + public onDeleteDataservice(): void { + const selectedService = this.filterDataservices().find((x) => x.getId() === this.dataserviceNameForDelete); + + // const itemsToDelete: Dataservice[] = ArrayUtils.intersect(this.selectedServices, this.filteredServices); + // const selectedService = itemsToDelete[0]; + + const dataserviceToDelete: NewDataservice = new NewDataservice(); + dataserviceToDelete.setId(selectedService.getId()); + + // Note: we can only doDelete selected items that we can see in the UI. + this.logger.log("[DataservicesPageComponent] Deleting selected Dataservice."); + const self = this; + this.dataserviceService + .deleteDataservice(dataserviceToDelete) + .subscribe( + () => { + self.removeDataserviceFromList(selectedService); + const link: string[] = [ DataservicesConstants.dataservicesRootPath ]; + self.logger.log("[CreateApiPageComponent] Navigating to: %o", link); + self.router.navigate(link).then(() => { + // nothing to do + }); + } + ); + } + + /** + * Filters and sorts the list of dataservices based on the user input + */ + public filterDataservices(): Dataservice[] { + // Clear the array first. + this.filteredServices.splice(0, this.filteredServices.length); + for (const dataservice of this.allServices) { + if (this.filter.accepts(dataservice)) { + this.filteredServices.push(dataservice); + } + } + this.filteredServices.sort( (c1: Dataservice, c2: Dataservice) => { + let rval = 0; + + if ( c1.getId() ) { + if ( c2.getId() ) { + // both dataservices have an ID + rval = c1.getId().localeCompare( c2.getId() ); + } else { + // c2 does not have an ID + rval = 1; + } + } else if ( c2.getId() ) { + // c1 does not have an ID and c2 does + rval = -1; + } + + if ( this.sortDirection === SortDirection.DESC ) { + rval *= -1; + } + + return rval; + }); + + this.selectedServices = ArrayUtils.intersect(this.selectedServices, this.filteredServices); + + return this.filteredServices; + } + + private removeDataserviceFromList(dataservice: Dataservice): void { + this.allServices.splice(this.allServices.indexOf(dataservice), 1); + this.filterDataservices(); + } +} diff --git a/ngapp/src/app/dataservices/dataservices.module.ts b/ngapp/src/app/dataservices/dataservices.module.ts new file mode 100644 index 00000000..b9e91610 --- /dev/null +++ b/ngapp/src/app/dataservices/dataservices.module.ts @@ -0,0 +1,59 @@ +/** + * @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 { CommonModule } from "@angular/common"; +import { NgModule } from "@angular/core"; +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { RouterModule } from "@angular/router"; +import { DataservicesCardsComponent } from "@dataservices/dataservices-cards/dataservices-cards.component"; +import { DataservicesListComponent } from "@dataservices/dataservices-list/dataservices-list.component"; +import { DataservicesRoutingModule } from "@dataservices/dataservices-routing.module"; +import { DataservicesComponent } from "@dataservices/dataservices.component"; +import { DataserviceService } from "@dataservices/shared/dataservice.service"; +import { CoreModule } from "@core/core.module"; +import { SharedModule } from "@shared/shared.module"; +import { PatternFlyNgModule } from "patternfly-ng"; +import { AddDataserviceWizardComponent } from "./add-dataservice-wizard/add-dataservice-wizard.component"; +import { AddDataserviceComponent } from "./add-dataservice/add-dataservice.component"; +import { LoggerService } from "@core/logger.service"; + +@NgModule({ + imports: [ + DataservicesRoutingModule, + CommonModule, + CoreModule, + SharedModule, + FormsModule, + ReactiveFormsModule, + RouterModule, + PatternFlyNgModule + ], + declarations: [ + DataservicesCardsComponent, + DataservicesComponent, + DataservicesListComponent, + AddDataserviceWizardComponent, + AddDataserviceComponent + ], + providers: [ + DataserviceService, + LoggerService + ], + exports: [ + ] +}) +export class DataservicesModule { } diff --git a/ngapp/src/app/dataservices/shared/dataservice.model.ts b/ngapp/src/app/dataservices/shared/dataservice.model.ts new file mode 100644 index 00000000..53f473bc --- /dev/null +++ b/ngapp/src/app/dataservices/shared/dataservice.model.ts @@ -0,0 +1,89 @@ +/** + * @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 { Identifiable } from "@shared/identifiable"; + +export class Dataservice implements Identifiable< string > { + + private keng__id: string; + private tko__description: string; + + /** + * @param {Object} json the JSON representation of a Dataservice + * @returns {Dataservice} the new Dataservice (never null) + */ + public static create( json: object = {} ): Dataservice { + const svc = new Dataservice(); + svc.setValues( json ); + return svc; + } + + constructor() { + // nothing to do + } + + /** + * @returns {string} the dataservice identifier (can be null) + */ + public getId(): string { + return this.keng__id; + } + + /** + * @returns {string} the dataservice description (can be null) + */ + public getDescription(): string { + return this.tko__description; + } + + /** + * @returns {string} the dataservice dataPath (can be null) + */ + public getDataPath(): string { + return "/tko:komodo/tko:workspace/dsbUser/" + this.keng__id; + } + + /** + * @returns {string} the dataservice type name (can be null) + */ + public getType(): string { + return "Dataservice"; + } + + /** + * @param {string} id the dataservice identifier (optional) + */ + public setId( id?: string ): void { + this.keng__id = id ? id : null; + } + + /** + * @param {string} id the dataservice description (optional) + */ + public setDescription( description?: string ): void { + this.tko__description = description ? description : null; + } + + /** + * Set all object values using the supplied Dataservice json + * @param {Object} values + */ + public setValues(values: object = {}): void { + Object.assign(this, values); + } + +} diff --git a/ngapp/src/app/dataservices/shared/dataservice.service.spec.ts b/ngapp/src/app/dataservices/shared/dataservice.service.spec.ts new file mode 100644 index 00000000..ea4f4099 --- /dev/null +++ b/ngapp/src/app/dataservices/shared/dataservice.service.spec.ts @@ -0,0 +1,18 @@ +import { inject, TestBed } from "@angular/core/testing"; +import { HttpModule } from "@angular/http"; +import { DataserviceService } from "@dataservices/shared/dataservice.service"; +import { LoggerService } from "@core/logger.service"; + +describe("DataserviceService", () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ HttpModule ], + providers: [DataserviceService, LoggerService] + }); + }); + + it("should be created", inject([DataserviceService, LoggerService], + ( service: DataserviceService ) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/ngapp/src/app/dataservices/shared/dataservice.service.ts b/ngapp/src/app/dataservices/shared/dataservice.service.ts new file mode 100644 index 00000000..2d564259 --- /dev/null +++ b/ngapp/src/app/dataservices/shared/dataservice.service.ts @@ -0,0 +1,80 @@ +/** + * @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 { Injectable } from "@angular/core"; +import { Http } from "@angular/http"; +import { Dataservice } from "@dataservices/shared/dataservice.model"; +import { DataservicesConstants } from "@dataservices/shared/dataservices-constants"; +import { NewDataservice } from "@dataservices/shared/new-dataservice.model"; +import { ApiService } from "@core/api.service"; +import { LoggerService } from "@core/logger.service"; +import { environment } from "@environments/environment"; +import { Observable } from "rxjs/Observable"; + +@Injectable() +export class DataserviceService extends ApiService { + + private http: Http; + + constructor( http: Http, logger: LoggerService ) { + super( logger ); + this.http = http; + } + + /** + * Get the dataservices from the komodo rest interface + * @returns {Observable} + */ + public getAllDataservices(): Observable { + return this.http + .get(environment.komodoWorkspaceUrl + DataservicesConstants.dataservicesRootPath, this.getAuthRequestOptions()) + .map((response) => { + const dataservices = response.json(); + return dataservices.map((dataservice) => Dataservice.create( dataservice )); + }) + .catch( ( error ) => this.handleError( error ) ); + } + + /** + * Create a dataservice via the komodo rest interface + * @param {NewDataservice} dataservice + * @returns {Observable} + */ + public createDataservice(dataservice: NewDataservice): Observable { + return this.http + .post(environment.komodoWorkspaceUrl + DataservicesConstants.dataservicesRootPath + "/" + dataservice.getId(), + dataservice, this.getAuthRequestOptions()) + .map((response) => { + return new Dataservice(); + }) + .catch( ( error ) => this.handleError( error ) ); + } + + /** + * Delete a dataservice via the komodo rest interface + * @param {NewDataservice} dataservice + * @returns {Observable} + */ + public deleteDataservice(dataservice: NewDataservice): Observable { + return this.http + .delete(environment.komodoWorkspaceUrl + DataservicesConstants.dataservicesRootPath + "/" + dataservice.getId(), + this.getAuthRequestOptions()) + .map((response) => null) + .catch( ( error ) => this.handleError( error ) ); + } + +} diff --git a/ngapp/src/app/dataservices/shared/dataservices-constants.ts b/ngapp/src/app/dataservices/shared/dataservices-constants.ts new file mode 100644 index 00000000..3c99d8f4 --- /dev/null +++ b/ngapp/src/app/dataservices/shared/dataservices-constants.ts @@ -0,0 +1,20 @@ +/** + * @license + * Copyright 2017 JBoss Inc + * + * Licensed under the Apache License, / + * 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 DataservicesConstants { + + public static readonly dataservicesRootRoute = "dataservices"; + public static readonly dataservicesRootPath = "/" + DataservicesConstants.dataservicesRootRoute; + + public static readonly addDataserviceRoute = DataservicesConstants.dataservicesRootRoute + "/add-dataservice"; + public static readonly addDataservicePath = DataservicesConstants.dataservicesRootPath + "/add-dataservice"; +} diff --git a/ngapp/src/app/dataservices/shared/mock-dataservice.service.ts b/ngapp/src/app/dataservices/shared/mock-dataservice.service.ts new file mode 100644 index 00000000..2a85806c --- /dev/null +++ b/ngapp/src/app/dataservices/shared/mock-dataservice.service.ts @@ -0,0 +1,55 @@ +import { Injectable } from "@angular/core"; +import { Http } from "@angular/http"; +import { Dataservice } from "@dataservices/shared/dataservice.model"; +import { DataserviceService } from "@dataservices/shared/dataservice.service"; +import { NewDataservice } from "@dataservices/shared/new-dataservice.model"; +import { LoggerService } from "@core/logger.service"; +import "rxjs/add/observable/of"; +import "rxjs/add/observable/throw"; +import "rxjs/add/operator/catch"; +import "rxjs/add/operator/map"; +import { Observable } from "rxjs/Observable"; + +@Injectable() +export class MockDataserviceService extends DataserviceService { + + private newDataservice = new NewDataservice(); + private serv1 = new Dataservice(); + private serv2 = new Dataservice(); + private serv3 = new Dataservice(); + private services: Dataservice[] = [this.serv1, this.serv2, this.serv3]; + + constructor( http: Http, logger: LoggerService ) { + super(http, logger); + this.serv1.setId("serv1"); + this.serv2.setId("serv2"); + this.serv3.setId("serv3"); + } + + /** + * Get the dataservices from the komodo rest interface + * @returns {Observable} + */ + public getAllDataservices(): Observable { + return Observable.of(this.services); + } + + /** + * Create a dataservice via the komodo rest interface + * @param {NewDataservice} dataservice + * @returns {Observable} + */ + public createDataservice(dataservice: NewDataservice): Observable { + return Observable.of(this.newDataservice); + } + + /** + * Delete a dataservice via the komodo rest interface + * @param {NewDataservice} dataservice + * @returns {Observable} + */ + public deleteDataservice(dataservice: NewDataservice): Observable { + return Observable.of(this.newDataservice); + } + +} diff --git a/ngapp/src/app/dataservices/shared/new-dataservice.model.ts b/ngapp/src/app/dataservices/shared/new-dataservice.model.ts new file mode 100644 index 00000000..499b6c27 --- /dev/null +++ b/ngapp/src/app/dataservices/shared/new-dataservice.model.ts @@ -0,0 +1,60 @@ +/** + * @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 NewDataservice { + + private keng__id: string; + private keng__dataPath: string; + private keng__kType: string; + private tko__description: string; + + /** + * Constructor + */ + constructor() { + this.keng__kType = "Dataservice"; + } + + /** + * @returns {string} the dataservice name (can be null) + */ + public getId(): string { + return this.keng__id; + } + + /** + * @returns {string} the dataservice description (can be null) + */ + public getDescription(): string { + return this.tko__description; + } + + /** + * @param {string} name the dataservice name + */ + public setId( name: string ): void { + this.keng__id = name; + this.keng__dataPath = "/tko:komodo/tko:workspace/dsbUser/" + name; + } + + /** + * @param {string} description the dataservice description (optional) + */ + public setDescription( description?: string ): void { + this.tko__description = description ? description : null; + } +} diff --git a/ngapp/tsconfig.json b/ngapp/tsconfig.json index 107cad33..ca20e4b5 100644 --- a/ngapp/tsconfig.json +++ b/ngapp/tsconfig.json @@ -7,6 +7,7 @@ "@app/*": ["app/*"], "@connections/*": ["app/connections/*"], "@core/*": ["app/core/*"], + "@dataservices/*": ["app/dataservices/*"], "@environments/*": ["environments/*"], "@shared/*": ["app/shared/*"] },