-
-
-
+
@@ -186,7 +214,7 @@
{{searchMsg}}
{{i18n.runningOdataQuery}}
-
diff --git a/ngapp/src/app/dataservices/odata-control/odata-control.component.spec.ts b/ngapp/src/app/dataservices/odata-control/odata-control.component.spec.ts
deleted file mode 100644
index f51ecfa7..00000000
--- a/ngapp/src/app/dataservices/odata-control/odata-control.component.spec.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import { async, ComponentFixture, TestBed } from "@angular/core/testing";
-
-import { FormsModule } from "@angular/forms";
-import { HttpModule } from "@angular/http";
-import { AppSettingsService } from "@core/app-settings.service";
-import { LoggerService } from "@core/logger.service";
-import { MockAppSettingsService } from "@core/mock-app-settings.service";
-import { Dataservice } from "@dataservices/shared/dataservice.model";
-import { DataserviceService } from "@dataservices/shared/dataservice.service";
-import { MockDataserviceService } from "@dataservices/shared/mock-dataservice.service";
-import { MockVdbService } from "@dataservices/shared/mock-vdb.service";
-import { NotifierService } from "@dataservices/shared/notifier.service";
-import { VdbService } from "@dataservices/shared/vdb.service";
-import { View } from "@dataservices/shared/view.model";
-import { CodemirrorModule } from "ng2-codemirror";
-import { PatternFlyNgModule } from "patternfly-ng";
-import { SqlControlComponent } from "./sql-control.component";
-
-describe("SqlControlComponent", () => {
- let component: SqlControlComponent;
- let fixture: ComponentFixture
;
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- imports: [ FormsModule, HttpModule, CodemirrorModule, PatternFlyNgModule ],
- declarations: [ SqlControlComponent ],
- providers: [
- AppSettingsService,
- LoggerService,
- NotifierService,
- { provide: AppSettingsService, useClass: MockAppSettingsService },
- { provide: DataserviceService, useClass: MockDataserviceService },
- { provide: VdbService, useClass: MockVdbService }
- ]
- })
- .compileComponents().then(() => {
- // nothing to do
- });
- }));
-
- beforeEach(() => {
- // select a dataservice before constructing component
- const service = TestBed.get( DataserviceService );
- let dataservices: Dataservice[];
- service.getAllDataservices().subscribe( ( values ) => { dataservices = values; } );
- // noinspection JSUnusedAssignment
- service.setSelectedDataservice( dataservices[ 0 ] );
-
- fixture = TestBed.createComponent(SqlControlComponent);
- component = fixture.componentInstance;
-
- // Set the inputs for the component
- component.viewSql = "SELECT * FROM views.View1";
- const view = new View();
- view.setName("views.View1");
- const views: View[] = [];
- views.push(view);
- component.serviceViews = views;
- component.selectedViews = views;
-
- fixture.detectChanges();
- });
-
- it("should be created", () => {
- console.log("========== [SqlControlComponent] should be created");
- expect(component).toBeTruthy();
- });
-});
diff --git a/ngapp/src/app/dataservices/odata-control/odata-control.component.ts b/ngapp/src/app/dataservices/odata-control/odata-control.component.ts
index b34f47a4..c6107b61 100644
--- a/ngapp/src/app/dataservices/odata-control/odata-control.component.ts
+++ b/ngapp/src/app/dataservices/odata-control/odata-control.component.ts
@@ -15,11 +15,12 @@
* limitations under the License.
*/
-import { Input, ViewEncapsulation } from "@angular/core";
+import { Input, ViewChild, ViewEncapsulation } from "@angular/core";
import { Component, OnChanges, SimpleChanges } from "@angular/core";
import { LoggerService } from "@core/logger.service";
-import { TableConfig, PaginationConfig, PaginationEvent } from "patternfly-ng";
import * as _ from "lodash";
+import "codemirror/mode/javascript/javascript.js";
+import "codemirror/mode/xml/xml.js";
import { ColumnData } from "@dataservices/shared/column-data.model";
import { DataserviceService } from "@dataservices/shared/dataservice.service";
import { Dataservice } from "@dataservices/shared/dataservice.model";
@@ -39,6 +40,9 @@ export class OdataControlComponent implements OnChanges {
@Input() dataserviceName: string;
+ @ViewChild('odataResultsEditor') resultsEditor: any;
+
+
private dataserviceService: DataserviceService;
private dataservice: Dataservice;
private logger: LoggerService;
@@ -47,20 +51,47 @@ export class OdataControlComponent implements OnChanges {
public searchMsg: string;
public searchMsgClasses: string[];
public searchInProgress: boolean;
- public showResultsTable: boolean;
- public rawResultRows: object;
- private odata: Odata;
+ public metadataFetchInProgress: boolean;
+
+ public odata: Odata;
+
+ private jsonFormat = {
+ mode: {
+ name: "javascript",
+ json: true,
+ statementIndent: 2
+ }
+ };
+
+ private xmlFormat = {
+ mode: {
+ name: "xml",
+ htmlMode: "false",
+ }
+ };
+
+ public resultsConfig = {
+ mode: {},
+ lineNumbers: true,
+ lineWrapping: true,
+ readOnly: false,
+ placeholder: "No results",
+ styleActiveLine: true,
+ tabSize: 2,
+ showCursorWhenSelecting: true,
+ theme: "neat"
+ };
//
- // Raw data rows retrieved from the query
+ // Format of the results
//
- public rawRows: any[];
+ public resultsType: string = "JSON";
- public resultTableConfig: TableConfig;
- public paginationConfig: PaginationConfig;
- public resultColumns: any[];
- public resultRows: any[] ;
+ //
+ // Results to show from the odata query
+ //
+ public results: string = null;
constructor( dataserviceService: DataserviceService, logger: LoggerService ) {
this.dataserviceService = dataserviceService;
@@ -74,42 +105,34 @@ export class OdataControlComponent implements OnChanges {
public ngOnChanges(changes: SimpleChanges): void {
this.dataservice = this.dataserviceService.getSelectedDataservice();
+ this.metadataFetchInProgress = false;
+
this.searchMsg = null;
this.searchMsgClasses = [];
this.searchInProgress = false;
- this.showResultsTable = false;
- this.rawResultRows = null;
this.odata = new Odata();
- this.paginationConfig = {
- pageNumber: 1,
- pageSize: 10,
- pageSizeIncrements: [5, 10, 15, 20, 25, 50]
- } as PaginationConfig;
-
- this.resultTableConfig = {
- paginationConfig: this.paginationConfig,
- } as TableConfig;
-
- this.rawRows = [];
- this.resultColumns = [];
- this.resultRows = [];
+ this.results = null;
const url = this.rootUrl + '/$metadata';
+ this.metadataFetchInProgress = true;
this.dataserviceService.odataGet(url)
.subscribe(
(response) => {
- if (! _.isEmpty(response)) {
- this.odata.metadata = response;
+ if (! _.isEmpty(response) && this.dataserviceService.isXML(response.value)) {
+ let xmlObject = this.dataserviceService.tryXMLParse(response.value);
+ this.odata.metadata = xmlObject;
this.odata.metadataFailure = false;
} else {
this.odata.metadata = '';
this.odata.metadataFailure = true;
}
+ this.metadataFetchInProgress = false;
},
(error) => {
this.odata.metadataFailure = true;
+ this.metadataFetchInProgress = false;
console.error('Failed to get odata metadata ' + error);
}
);
@@ -120,19 +143,19 @@ export class OdataControlComponent implements OnChanges {
*/
public get endPointUrl(): string {
if (this.odata.metadataFailure)
- return 'Not available';
+ return this.i18n.UrlNotAvailable;
if (_.isEmpty(this.odata.metadata))
- return 'Not available';
+ return this.i18n.UrlNotAvailable;
const baseUrl = this.rootUrl;
if (_.isEmpty(baseUrl))
- return 'Not Available';
+ return this.i18n.UrlNotAvailable;
const service = this.odata.entity;
if (_.isEmpty(service) || _.isEmpty(service.name))
- return 'Not Available';
+ return this.i18n.UrlNotAvailable;
let odataUrl = baseUrl + '/' + service.name;
@@ -152,6 +175,14 @@ export class OdataControlComponent implements OnChanges {
if (! _.isEmpty(orderBy))
odataUrl = odataUrl + orderBy;
+ //
+ // Append the result format but only if the
+ // query has not specified count (which is incompatible)
+ //
+ if (! _.isEmpty(this.resultsType) && this.odata.limit !== Odata.COUNT_ONLY) {
+ odataUrl = odataUrl + "$format=" + this.resultsType.toLowerCase();
+ }
+
if (odataUrl.endsWith('&') || odataUrl.endsWith('?'))
odataUrl = odataUrl.substring(0, odataUrl.length - 1);
@@ -166,12 +197,19 @@ export class OdataControlComponent implements OnChanges {
return ! _.isEmpty(this.odata.metadata);
}
+ public get metadataFailure(): boolean {
+ return this.odata.metadataFailure;
+ }
+
/**
* Can the query be enacted
* @returns {boolean} true if good to go, false otherwise
*/
public get canQuery(): boolean {
- return !_.isEmpty(this.odata.metadata) && !_.isEmpty(this.odata.entity);
+ return !_.isEmpty(this.odata.metadata) &&
+ !_.isEmpty(this.odata.entity) &&
+ this.endPointUrl !== this.i18n.UrlNotAvailable;
+
}
/**
@@ -346,11 +384,8 @@ export class OdataControlComponent implements OnChanges {
return [];
}
- private tableTitle(value: string): string {
- if (_.isEmpty(value))
- return value;
-
- return _.startCase(value);
+ public get showResults(): boolean {
+ return ! _.isEmpty(this.results);
}
/**
@@ -359,33 +394,14 @@ export class OdataControlComponent implements OnChanges {
public submitQuery(): void {
let url = this.endPointUrl;
- if (this.odata.limit !== Odata.COUNT_ONLY) {
- //
- // Json format cannot be used with $count
- //
- if (url.indexOf('?') > -1)
- // Already have parameters
- url = url + '&';
- else
- url = url + '?';
-
- url = url + "$format=json";
- }
-
- this.rawResultRows = null;
- this.resultColumns = [];
- this.resultRows = [];
- this.rawRows = [];
this.searchMsg = null;
this.searchMsgClasses = [];
this.searchInProgress = true;
- this.showResultsTable = false;
this.dataserviceService.odataGet(url)
.subscribe(
(response) => {
- this.showResultsTable = false;
- this.rawResultRows = null;
+ this.results = null;
// Remove the search progress spinner
this.searchInProgress = false;
@@ -411,66 +427,21 @@ export class OdataControlComponent implements OnChanges {
return;
}
- if (this.selectedColumns.length === 0) {
- //
- // No columns selected so choose all available
- //
- let resultCols = this.columns;
- for (let i = 0; i < this.columns.length; ++i) {
- const label = this.columns[i].name;
- const col = { canAutoResize: true,
- draggable: false,
- name: this.tableTitle(label),
- prop: label,
- resizable: true,
- sortable: true };
- this.resultColumns.push(col);
- }
+ const instance = this.resultsEditor.instance;
+ if (this.resultsType === "XML") {
+ instance.setOption('mode', this.xmlFormat.mode);
} else {
- for (var j = 0; j < this.selectedColumns.length; ++j) {
- const label = this.selectedColumns[j].name;
- const col = { canAutoResize: true,
- draggable: false,
- name: this.tableTitle(label),
- prop: label,
- resizable: true,
- sortable: true };
- this.resultColumns.push(col);
- }
- }
-
- for (let rowIndex = 0; rowIndex < response.value.length; rowIndex++) {
- const row = response.value[rowIndex];
- this.rawRows.push(row);
+ instance.setOption('mode', this.jsonFormat.mode);
}
- this.updateRows();
- this.showResultsTable = true;
+ this.results = response.value;
},
(error) => {
- this.showResultsTable = false;
+ this.results = null;
this.searchInProgress = false;
this.searchMsgClasses = ['odata-results-msg-error'];
this.searchMsg = 'Failed to get odata results ' + error;
}
);
}
-
- // Pagination
- handlePageSize($event: PaginationEvent): void {
- this.updateRows();
- }
-
- handlePageNumber($event: PaginationEvent): void {
- this.updateRows();
- }
-
- updateRows(): void {
- this.paginationConfig.totalItems = this.rawRows.length;
-
- this.resultRows = this.rawRows
- .slice(
- (this.paginationConfig.pageNumber - 1) * this.paginationConfig.pageSize, this.paginationConfig.totalItems)
- .slice(0, this.paginationConfig.pageSize);
- }
}
diff --git a/ngapp/src/app/dataservices/shared/dataservice.service.ts b/ngapp/src/app/dataservices/shared/dataservice.service.ts
index e532a504..8bd9ff63 100644
--- a/ngapp/src/app/dataservices/shared/dataservice.service.ts
+++ b/ngapp/src/app/dataservices/shared/dataservice.service.ts
@@ -41,6 +41,7 @@ import { ReplaySubject } from "rxjs/ReplaySubject";
import { Subject } from "rxjs/Subject";
import { Subscription } from "rxjs/Subscription";
import * as _ from "lodash";
+import * as vkbeautify from 'vkbeautify';
@Injectable()
export class DataserviceService extends ApiService {
@@ -496,12 +497,17 @@ export class DataserviceService extends ApiService {
const data = response.text();
let jobj = this.tryJsonParse(data);
if (_.isObject(jobj)) {
- return jobj;
- } else if (this.isXML(data)) {
- // convert the data to JSON and provide
- // it to the success function below
- let json = this.tryXMLParse(data);
- return json;
+ let json = JSON.stringify(jobj, null, 4);
+ return {
+ value: json
+ };
+ }
+
+ if (this.isXML(data)) {
+ const xml = vkbeautify.xml(data);
+ return {
+ value: xml
+ };
}
if (_.isEqual(data, "0")) {
diff --git a/ngapp/src/app/dataservices/sql-control/sql-control.component.css b/ngapp/src/app/dataservices/sql-control/sql-control.component.css
index 12616a9d..b2b4e2f2 100644
--- a/ngapp/src/app/dataservices/sql-control/sql-control.component.css
+++ b/ngapp/src/app/dataservices/sql-control/sql-control.component.css
@@ -42,6 +42,14 @@
padding-left: 0;
}
+/*
+ * Limit CodeMirror class changes to
+ * the sql-control-editor.
+ */
+.sql-control-editor .CodeMirror {
+ height: 200px;
+}
+
.sql-results-panel {
padding-left: 0;
padding-right: 0;
diff --git a/ngapp/src/styles.css b/ngapp/src/styles.css
index 6d5e4f1e..1772bae0 100644
--- a/ngapp/src/styles.css
+++ b/ngapp/src/styles.css
@@ -3,6 +3,7 @@
@import "~angular-tree-component/dist/angular-tree-component.css";
@import "~codemirror/lib/codemirror.css";
@import "~codemirror/theme/mdn-like.css";
+@import "~codemirror/theme/neat.css";
@import "~patternfly/dist/css/patternfly.css";
@import "~patternfly/dist/css/patternfly-additions.css";
@import "~patternfly-ng/dist/css/patternfly-ng.css";
@@ -268,14 +269,6 @@
text-align: left;
}
-/***************************************************************/
-/* CodeMirror Types */
-/***************************************************************/
-
-.CodeMirror {
- height: 200px;
-}
-
/**************************************************************/
/* PatternFly Types */
/**************************************************************/