KNOX-1040 - Added modification support: delete, discard changes, save changes
Project: http://git-wip-us.apache.org/repos/asf/knox/repo Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/cd336759 Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/cd336759 Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/cd336759 Branch: refs/heads/master Commit: cd336759970caaa6051e28b9204dfe46034c01de Parents: 5b57a51 Author: Phil Zampino <[email protected]> Authored: Fri Feb 2 22:01:48 2018 -0500 Committer: Phil Zampino <[email protected]> Committed: Wed Feb 7 09:43:00 2018 -0500 ---------------------------------------------------------------------- gateway-admin-ui/package.json | 2 +- .../src/app/resource-detail/descriptor.ts | 14 + .../resource-detail.component.html | 144 ++++++++- .../resource-detail.component.ts | 305 ++++++++++++++++--- .../src/app/resource/resource.component.ts | 5 + .../src/app/resource/resource.service.ts | 67 ++-- .../applications/admin-ui/app/index.html | 2 +- .../app/inline.1b8a794b441ffd968489.bundle.js | 1 - .../app/inline.1bf9c629c91002a65d30.bundle.js | 1 + .../app/main.10e7c17d5707ac382dbb.bundle.js | 1 - .../app/main.4689931d0eaf6a8f51c9.bundle.js | 1 + .../apache/knox/gateway/GatewayMessages.java | 3 + .../topology/impl/DefaultTopologyService.java | 98 +++++- .../DefaultRemoteConfigurationMonitor.java | 5 + .../org/apache/knox/gateway/util/KnoxCLI.java | 12 +- .../service/admin/TopologiesResource.java | 88 ++++-- .../monitor/RemoteConfigurationMonitor.java | 5 + .../gateway/GatewayAdminTopologyFuncTest.java | 9 - 18 files changed, 644 insertions(+), 119 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-admin-ui/package.json ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/package.json b/gateway-admin-ui/package.json index 9f6548c..8fbfeb5 100644 --- a/gateway-admin-ui/package.json +++ b/gateway-admin-ui/package.json @@ -26,7 +26,7 @@ "ng2-ace-editor": "0.3.3", "ng2-bs3-modal": "^0.13.0", "ts-helpers": "^1.1.1", - "jquery": "^1.12.4", + "jquery": ">=3.0.0", "js-yaml": "^3.10.0", "popper.js": "^1.12.9" }, http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-admin-ui/src/app/resource-detail/descriptor.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/resource-detail/descriptor.ts b/gateway-admin-ui/src/app/resource-detail/descriptor.ts index c407f96..998265a 100644 --- a/gateway-admin-ui/src/app/resource-detail/descriptor.ts +++ b/gateway-admin-ui/src/app/resource-detail/descriptor.ts @@ -25,6 +25,8 @@ export class Descriptor { providerConfig: string; services: Service[]; + private dirty: boolean = false; + getServiceParamKeys(service: Service): string[] { let result = []; for(let key in service.params){ @@ -40,7 +42,19 @@ export class Descriptor { } setProviderConfig(providerConfigRef: string) { + console.debug('Descriptor --> setProviderConfig() --> ' + providerConfigRef); + if (providerConfigRef !== this.providerConfig) { this.providerConfig = providerConfigRef; + this.setDirty(); + } + } + + setDirty() { + this.dirty = true; + } + + public isDirty(): boolean { + return this.dirty; } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-admin-ui/src/app/resource-detail/resource-detail.component.html ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/resource-detail/resource-detail.component.html b/gateway-admin-ui/src/app/resource-detail/resource-detail.component.html index 3303341..96f6efb 100644 --- a/gateway-admin-ui/src/app/resource-detail/resource-detail.component.html +++ b/gateway-admin-ui/src/app/resource-detail/resource-detail.component.html @@ -10,24 +10,30 @@ <div *ngIf="hasSelectedResource()"> <div class="panel panel-default" *ngFor="let provider of providers"> <span [class]="'clickable glyhpicon glyphicon-' + (provider.show ? 'minus' : 'plus')" - (click)="provider.show = !provider.show"></span> + (click)="provider.show = !provider.show"></span> <span><b>{{ provider.name }}</b></span> - <span class="glyphicon glyphicon-remove-sign pull-right" data-toggle="tooltip" [title]="'Remove ' + provider.name"></span> + <span class="clickable glyphicon glyphicon-remove-sign pull-right" + [title]="'Remove ' + provider.name" + (click)="onRemoveProvider(provider.name)" + data-toggle="tooltip"></span> <div *ngIf="provider.show"> <b>Role</b> {{ provider.role }}<br> <b>Enabled</b> {{ provider.enabled }}<br> <div> <span> </span> - <span [class]="'clickable glyhpicon glyphicon-' + (provider.paramDetails ? 'minus' : 'plus')" - (click)="provider.paramDetails = !provider.paramDetails"></span> + <span [class]="'clickable glyhpicon glyphicon-' + (provider.showParamDetails ? 'minus' : 'plus')" + (click)="provider.showParamDetails = !provider.showParamDetails"></span> <span><b>Params</b></span> - <span class="glyphicon glyphicon-plus-sign pull-right" data-toggle="tooltip" title="Add Param"></span> + <span class="clickable glyphicon glyphicon-plus-sign pull-right" data-toggle="tooltip" title="Add Param"></span> - <div class="panel panel-default table-responsive" *ngIf="provider.paramDetails"> + <div class="panel panel-default table-responsive" *ngIf="provider.showParamDetails"> <table class="table table-sm"> <tr *ngFor="let key of getParamKeys(provider)"> <td> - <span class="glyphicon glyphicon-remove-sign"></span> + <span class="clickable glyphicon glyphicon-remove-sign" + title="Remove Param" + (click)="onRemoveProviderParam(provider, key)" + data-toggle="tooltip"></span> </td> <td><b>{{ key }}</b></td> <td>{{ provider.params[key] }}</td> @@ -39,6 +45,35 @@ <br><br> </div> </div> + + <div> <!-- Provider Configuration Modification Buttons --> + <button type="button" + title="Remove Provider Configuration" + class="btn btn-default btn-sm pull-left" + (click)="deleteConfirmModal.open('md')" + data-toggle="tooltip"> + <span class="clickable glyphicon glyphicon-trash"></span> + </button> + <span class="pull-right"> + <button type="button" + title="Discard Changes" + class="btn btn-default btn-sm" + [disabled]="!changedProviders" + (click)="discardConfirmModal.open('md')" + data-toggle="tooltip"> + <span class="glyphicon glyphicon-refresh"></span> + </button> + <span> </span> + <button type="button" + title="Update Provider Configuration" + class="btn btn-default btn-sm" + [disabled]="!changedProviders" + (click)="persistChanges()" + data-toggle="tooltip"> + <span class="glyphicon glyphicon-floppy-disk"></span> + </button> + </span> + </div> </div> <!-- Descriptor Details --> @@ -49,13 +84,19 @@ <div class="panel panel-default col-md-12"> <span class="col-md-4"><b>Provider Configuration</b></span> <span class="col-md-7"> - <input type="text" class="col-md-sm form-control input-sm" [(ngModel)]="descriptor.providerConfig" id="textbox"/> + <input type="text" + id="textbox" + class="col-md-sm form-control input-sm" + (input)="descriptor.setDirty()" + [(ngModel)]="descriptor.providerConfig"/> </span> <span class="col-md-1 pull-left"> <button id="chooseProviderConfig" class="btn btn-default btn-xs glyphicon glyphicon-edit" (click)="chooseProviderConfigModal.open(descriptor, 'sm')" - type="submit"> + type="submit" + data-toggle="tooltip" + title="Choose Provider Configuration"> </button> </span> </div> @@ -68,25 +109,32 @@ <span [class]="'clickable glyhpicon glyphicon-' + (showServiceDetails ? 'minus' : 'plus')" (click)="showServiceDetails = !showServiceDetails"></span> <span><b>Services</b></span> - <span class="glyphicon glyphicon-plus-sign pull-right" data-toggle="tooltip" title="Add Service"></span> + <span class="clickable glyphicon glyphicon-plus-sign pull-right" data-toggle="tooltip" title="Add Service"></span> <div class="col-md-12 table-responsive" *ngIf="showServiceDetails"> <table class="table table-striped table-sm"> <tr *ngFor="let service of descriptor.services"> <td> <div> <span><b>{{ service.name }}</b></span> - <span class="glyphicon glyphicon-remove-sign pull-right" data-toggle="tooltip" [title]="'Remove ' + service.name"></span> + <span class="clickable glyphicon glyphicon-remove-sign pull-right" + [title]="'Remove ' + service.name" + (click)="onRemoveDescriptorService(service.name)" + data-toggle="tooltip"></span> </div> <div> <span [class]="'clickable glyhpicon glyphicon-' + (service.showParams ? 'minus' : 'plus')" (click)="service.showParams = !service.showParams"></span> <span>Params</span><span> </span><span class="glyphicon glyphicon-plus-sign" data-toggle="tooltip" title="Add param"></span> - <!--<span class="btn btn-default btn-xs" data-toggle="tooltip" title="Add param">add</span>--> </div> <div class="table-responsive" *ngIf="service.showParams"> <table class="table table-sm" *ngIf="descriptor.getServiceParamKeys(service).length > 0"> <tr *ngFor="let paramKey of descriptor.getServiceParamKeys(service)"> - <td width="5%"><span class="glyphicon glyphicon-remove-sign"></span></td> + <td width="5%"> + <span class="clickable glyphicon glyphicon-remove-sign" + title="Remove Param" + (click)="onRemoveDescriptorServiceParam(service.name, paramKey)" + data-toggle="tooltip"></span> + </td> <td width="30%"><b>{{ paramKey }}</b></td> <td width="65%" align="left">{{ descriptor.getServiceParamValue(service, paramKey) }}</td> </tr> @@ -100,7 +148,12 @@ <div class="table-responsive" *ngIf="service.showUrls"> <table class="table table-sm" *ngIf="service.urls && service.urls.length > 0"> <tr *ngFor="let url of service.urls"> - <td width="5%"><span class="glyphicon glyphicon-remove-sign"></span></td> + <td width="5%"> + <span class="clickable glyphicon glyphicon-remove-sign" + title="Remove URL" + (click)="onRemoveDescriptorServiceURL(service.name, url)" + data-toggle="tooltip"></span> + </td> <td width="95%">{{ url }}</td> </tr> </table> @@ -125,6 +178,69 @@ </div> </div> </div> + + <div> <!-- Descriptor Modification Buttons --> + <button type="button" + title="Remove Descriptor" + class="btn btn-default btn-sm pull-left" + (click)="deleteConfirmModal.open('md')" + data-toggle="tooltip"> + <span class="clickable glyphicon glyphicon-trash"></span> + </button> + <span class="pull-right"> + <button type="button" + title="Discard Changes" + class="btn btn-default btn-sm" + [disabled]="!descriptor.isDirty()" + (click)="discardConfirmModal.open('md')" + data-toggle="tooltip"> + <span class="glyphicon glyphicon-refresh"></span> + </button> + <span> </span> + <button type="button" + title="Update Descriptor" + class="btn btn-default btn-sm" + [disabled]="!descriptor.isDirty()" + (click)="persistChanges()" + data-toggle="tooltip"> + <span class="glyphicon glyphicon-floppy-disk"></span> + </button> + </span> + </div> + </div> + + <div> <!-- Confirmation Modal Dialogs --> + <bs-modal (onClose)="deleteResource()" #deleteConfirmModal> + <bs-modal-header [showDismiss]="true"> + <h4 class="modal-title">Deleting {{getTitleSubject()}}</h4> + </bs-modal-header> + <bs-modal-body>Are you sure you want to delete <b>{{resourceService.getResourceDisplayName(resource)}}</b>?</bs-modal-body> + <bs-modal-footer> + <button type="button" + class="btn btn-default btn-sm" + data-dismiss="deleteConfirmModal" + (click)="deleteConfirmModal.dismiss()">Cancel</button> + <button type="button" + class="btn btn-primary btn-sm" + (click)="deleteConfirmModal.close()">Ok</button> + </bs-modal-footer> + </bs-modal> + + <bs-modal (onClose)="discardChanges()" #discardConfirmModal> + <bs-modal-header [showDismiss]="true"> + <h4 class="modal-title">Discard {{getTitleSubject()}} Changes</h4> + </bs-modal-header> + <bs-modal-body>Are you sure you want to discard <b>{{resourceService.getResourceDisplayName(resource)}}</b> changes?</bs-modal-body> + <bs-modal-footer> + <button type="button" + class="btn btn-default btn-sm" + data-dismiss="revertConfirmModal" + (click)="discardConfirmModal.dismiss()">Cancel</button> + <button type="button" + class="btn btn-primary btn-sm" + (click)="discardConfirmModal.close()">Ok</button> + </bs-modal-footer> + </bs-modal> </div> </div> http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-admin-ui/src/app/resource-detail/resource-detail.component.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/resource-detail/resource-detail.component.ts b/gateway-admin-ui/src/app/resource-detail/resource-detail.component.ts index 3a85574..5d9e2e7 100644 --- a/gateway-admin-ui/src/app/resource-detail/resource-detail.component.ts +++ b/gateway-admin-ui/src/app/resource-detail/resource-detail.component.ts @@ -26,6 +26,7 @@ import 'brace/theme/monokai'; import 'brace/mode/xml'; import { ProviderConfigSelectorComponent } from "../provider-config-selector/provider-config-selector.component"; +import { ResourceTypesService } from "../resourcetypes/resourcetypes.service"; @Component({ @@ -47,46 +48,30 @@ export class ResourceDetailComponent implements OnInit { resourceContent: string; providers: Array<ProviderConfig>; + changedProviders: Array<ProviderConfig>; descriptor: Descriptor; - - availableProviderConfigs: Resource[]; + changedDescriptor: Descriptor; @ViewChild('choosePC') chooseProviderConfigModal: ProviderConfigSelectorComponent; - constructor(private resourceService: ResourceService) { + constructor(private resourceService: ResourceService, private resourceTypesService: ResourceTypesService) { } ngOnInit() { - this.resourceService.getResources('Provider Configurations').then(pcs => { - this.availableProviderConfigs = pcs; - }); - this.resourceService.selectedResourceType$.subscribe(type => this.setResourceType(type)); this.resourceService.selectedResource$.subscribe(value => this.setResource(value)); } - get self() { - return this; - } - setResourceType(resType: string) { - if (resType !== this.resourceType) { - - if (resType === 'Descriptors') { - // Update the available provider configurations if we're dealing with descriptors - this.resourceService.getResources("Provider Configurations").then(result => this.availableProviderConfigs = result); - } - - // Clear the current resource details - if (this.resource) {this.resource.name = '';} // This clears the details title when the type context changes - this.resource = ResourceDetailComponent.emptyResource; - this.providers = null; - this.descriptor = ResourceDetailComponent.emptyDescriptor; - this.resourceContent = ''; // Clear the content area - this.resourceType = resType; - } + // Clear the current resource details + if (this.resource) {this.resource.name = '';} // This clears the details title when the type context changes + this.resource = ResourceDetailComponent.emptyResource; + this.providers = null; + this.descriptor = ResourceDetailComponent.emptyDescriptor; + this.resourceContent = ''; // Clear the content area + this.resourceType = resType; } setResource(res: Resource) { @@ -125,21 +110,30 @@ export class ResourceDetailComponent implements OnInit { } else if (res.name.endsWith('xml')) { // Parse the XML representation parseString(this.resourceContent, (err, result) => { + // Parsing the XML is a bit less straight-forward let tempProviders = new Array<ProviderConfig>(); result['gateway'].provider.forEach(entry => { - let providerConfig: ProviderConfig = entry; - let params = {}; - entry.param.forEach(param => { - params[param.name] = param.value; - }); - providerConfig.params = params; + let providerConfig: ProviderConfig = new ProviderConfig(); + providerConfig.role = entry.role[0]; + providerConfig.name = entry.name[0]; + providerConfig.enabled = entry.enabled[0]; + + // There may not be params + if (entry.param) { + let params = {}; + for (let i = 0; i < entry.param.length; i++) { + let param = entry.param[i]; + params[param.name[0]] = param.value[0]; + } + providerConfig.params = params; + } tempProviders.push(providerConfig); }); this.providers = tempProviders; }); } } catch (e) { - console.error('Error parsing ' + res.name + ' content: ' + e); + console.error('ResourceDetailComponent --> setProviderConfigContent() --> Error parsing ' + res.name + ' content: ' + e); } } } @@ -167,7 +161,248 @@ export class ResourceDetailComponent implements OnInit { } this.descriptor = tempDesc; } catch (e) { - console.error('Error parsing '+ res.name + ' content: ' + e); + console.error('ResourceDetailComponent.setDescriptorContent: Error parsing '+ res.name + ' content: ' + e); + } + } + } + + persistChanges() { + switch(this.resourceType) { + case 'Provider Configurations' : { + this.persistProviderConfiguration(); + break; + } + case 'Descriptors': { + this.persistDescriptor(); + } + } + this.setResource(this.resource); // Refresh the detail presentation to reflect the saved state + } + + persistProviderConfiguration() { + let content; + let ext = this.resource.name.split('.').pop(); + switch(ext) { + case 'json': { + content = this.serializeProviderConfiguration(this.providers, 'json'); + break; + } + case 'yaml': + case 'yml': { + content = this.serializeProviderConfiguration(this.providers, 'yaml'); + break; + } + case 'xml': { + // We're not going to bother serializing XML. Rather, delete the original XML resource, and replace it + // with JSON + console.debug('Replacing XML provider configuration ' + this.resource.name + ' with JSON...'); + + // Generate the JSON representation of the updated provider configuration + content = this.serializeProviderConfiguration(this.providers, 'json'); + + let replacementResource = new Resource(); + replacementResource.name = this.resource.name.slice(0, -4) + '.json'; + replacementResource.href = this.resource.href; + + // Delete the XML resource + this.resourceService.deleteResource(this.resource.href).then(() => { + // Save the updated content + this.resourceService.saveResource(replacementResource, content).then(() => { + // Refresh the presentation + this.changedProviders = null; + this.setResource(replacementResource); + }); + }); + break; + } + } + + // For the non-XML provider configuration cases, simply save the changes + if (ext !== 'xml') { + // Save the updated content + this.resourceService.saveResource(this.resource, content).then(result => { + // Refresh the presentation + this.changedProviders = null; + this.setResource(this.resource); + }); + } + } + + + persistDescriptor() { + let content; + let ext = this.resource.name.split('.').pop(); + switch(ext) { + case 'json': { + content = this.serializeDescriptor(this.descriptor, 'json'); + break; + } + case 'yaml': + case 'yml': { + content = this.serializeDescriptor(this.descriptor, 'yaml'); + break; + } + } + this.resourceService.saveResource(this.resource, content); + + // Refresh the presentation + this.setResource(this.resource); + } + + + serializeDescriptor(desc: Descriptor, format: string): string { + let serialized: string; + + let tmp = {}; + tmp['discovery-address'] = desc.discoveryAddress; + tmp['discovery-user'] = desc.discoveryUser; + if (desc.discoveryPassAlias) { + tmp['discovery-pwd-alias'] = desc.discoveryPassAlias; + } + tmp['cluster'] = desc.discoveryCluster; + tmp['provider-config-ref'] = desc.providerConfig; + tmp['services'] = desc.services; + + // Scrub any UI-specific service properties + for(let service of tmp['services']) { + if(service.hasOwnProperty('showParams')) { + delete service.showParams; + } + if(service.hasOwnProperty('showUrls')) { + delete service.showUrls; + } + } + + switch(format) { + case 'json': { + serialized = JSON.stringify(tmp, null, 2); + break; + } + case 'yaml': { + let yaml = require('js-yaml'); + serialized = '---\n' + yaml.dump(tmp); + break; + } + } + + return serialized; + } + + + serializeProviderConfiguration(providers: Array<ProviderConfig>, format: string): string { + let serialized: string; + + let tmp = {}; + tmp['providers'] = providers; + + // Scrub any UI-specific provider properties + for(let pc of tmp['providers']) { + if(pc.hasOwnProperty('show')) { + delete pc.show; + } + if(pc.hasOwnProperty('showParamDetails')) { + delete pc.showParamDetails; + } + } + + switch(format) { + case 'json': { + serialized = JSON.stringify(tmp, null, 2); + break; + } + case 'yaml': { + let yaml = require('js-yaml'); + serialized = '---\n' + yaml.dump(tmp); + break; + } + } + + return serialized; + } + + + discardChanges() { + this.changedProviders = null; + this.setResource(this.resource); // Reset the resource to refresh to the original content + } + + + deleteResource() { + this.resourceService.deleteResource(this.resource.href); + this.resourceTypesService.selectResourceType(this.resourceType); // This refreshes the list of resources + } + + + onRemoveProvider(name: string) { + console.debug('ResourceDetailComponent --> onRemoveProvider() --> ' + name); + for(let i = 0; i < this.providers.length; i++) { + if(this.providers[i].name === name) { + this.providers.splice(i, 1); + break; + } + } + this.changedProviders = this.providers; + } + + + onRemoveProviderParam(pc: ProviderConfig, paramName: string) { + console.debug('ResourceDetailComponent --> onRemoveProviderParam() --> ' + pc.name + ' --> ' + paramName); + if(pc.params.hasOwnProperty(paramName)) { + delete pc.params[paramName]; + } + this.changedProviders = this.providers; + } + + + onRemoveDescriptorService(serviceName: string) { + console.debug('ResourceDetailComponent --> onRemoveDescriptorService() --> ' + serviceName); + for(let i = 0; i < this.descriptor.services.length; i++) { + if(this.descriptor.services[i].name === serviceName) { + this.descriptor.services.splice(i, 1); + this.descriptor.setDirty(); + break; + } + } + } + + + onRemoveDescriptorServiceParam(serviceName: string, paramName: string) { + console.debug('ResourceDetailComponent --> onRemoveDescriptorServiceParam() --> ' + serviceName + ' : ' + paramName); + let done: boolean = false; + for(let i = 0; i < this.descriptor.services.length; i++) { + if(this.descriptor.services[i].name === serviceName) { + let service = this.descriptor.services[i]; + if(service.params.hasOwnProperty(paramName)) { + delete service.params[paramName]; + this.descriptor.setDirty(); + done = true; + break; + } + } + if (done) { // Stop checking services if it has already been handled + break; + } + } + } + + + onRemoveDescriptorServiceURL(serviceName: string, serviceUrl: string) { + console.debug('ResourceDetailComponent --> onRemoveDescriptorServiceParam() --> ' + serviceName + ' : ' + serviceUrl); + let done: boolean = false; + for(let i = 0; i < this.descriptor.services.length; i++) { + if(this.descriptor.services[i].name === serviceName) { + let service = this.descriptor.services[i]; + for(let j = 0; j < service.urls.length; j++) { + if(service.urls[j] === serviceUrl) { + service.urls.splice(j, 1); + this.descriptor.setDirty(); + done = true; + break; + } + } + } + if (done) { // Stop checking services if it has already been handled + break; } } } @@ -193,10 +428,12 @@ export class ResourceDetailComponent implements OnInit { return result; } + hasSelectedResource(): boolean { return Boolean(this.resource) && Boolean(this.resource.name); } + getTitleSubject(): string { switch(this.resourceType) { case 'Topologies': { http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-admin-ui/src/app/resource/resource.component.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/resource/resource.component.ts b/gateway-admin-ui/src/app/resource/resource.component.ts index 264e03e..e96bbde 100644 --- a/gateway-admin-ui/src/app/resource/resource.component.ts +++ b/gateway-admin-ui/src/app/resource/resource.component.ts @@ -46,6 +46,11 @@ export class ResourceComponent implements OnInit { setResourceType(resType: string) { + //console.debug('ResourceComponent--> setResourceType --> ' + resType); + + // Clear the selected resource, so it can be removed from the list on refresh if necessary + this.selectedResource = null; + this.resourceType = resType; this.resourceService.selectedResourceType(this.resourceType); this.resources = []; // Clear the table before loading the new resources http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-admin-ui/src/app/resource/resource.service.ts ---------------------------------------------------------------------- diff --git a/gateway-admin-ui/src/app/resource/resource.service.ts b/gateway-admin-ui/src/app/resource/resource.service.ts index d6c8327..fd698a0 100644 --- a/gateway-admin-ui/src/app/resource/resource.service.ts +++ b/gateway-admin-ui/src/app/resource/resource.service.ts @@ -19,6 +19,8 @@ import { HttpHeaders, HttpClient} from '@angular/common/http'; import 'rxjs/add/operator/toPromise'; import { Subject } from 'rxjs/Subject'; import { Resource } from './resource'; +import {ProviderConfig} from "../resource-detail/provider-config"; +import {Descriptor} from "../resource-detail/descriptor"; @Injectable() @@ -38,6 +40,12 @@ export class ResourceService { changedResourceSource = new Subject<string>(); changedResource$ = this.changedResourceSource.asObservable(); + changedProviderConfigurationSource = new Subject<Array<ProviderConfig>>(); + changedProviderConfiguration$ = this.changedProviderConfigurationSource.asObservable(); + + changedDescriptorSource = new Subject<Descriptor>(); + changedDescriptor = this.changedDescriptorSource.asObservable(); + constructor(private http: HttpClient) { } getResources(resType: string): Promise<Resource[]> { @@ -56,7 +64,7 @@ export class ResourceService { getProviderConfigResources(): Promise<Resource[]> { let headers = this.addJsonHeaders(new HttpHeaders()); - this.logHeaders(headers); + //this.logHeaders(headers); return this.http.get(this.providersUrl, { headers: headers }) .toPromise() .then(response => response['items'] as Resource[]) @@ -65,7 +73,7 @@ export class ResourceService { getDescriptorResources(): Promise<Resource[]> { let headers = this.addJsonHeaders(new HttpHeaders()); - this.logHeaders(headers); + //this.logHeaders(headers); return this.http.get(this.descriptorsUrl, { headers: headers }) .toPromise() .then(response => response['items'] as Resource[]) @@ -74,7 +82,7 @@ export class ResourceService { getTopologyResources(): Promise<Resource[]> { let headers = this.addJsonHeaders(new HttpHeaders()); - this.logHeaders(headers); + //this.logHeaders(headers); return this.http.get(this.topologiesUrl, { headers: headers }) .toPromise() .then(response => response['topologies'].topology as Resource[]) @@ -84,44 +92,46 @@ export class ResourceService { getResource(resType: string, res : Resource): Promise<string> { let headers = new HttpHeaders(); headers = (resType === 'Topologies') ? this.addXmlHeaders(headers) : this.addHeaders(headers, res.name); - this.logHeaders(headers); + //this.logHeaders(headers); - return this.http.get(res.href, { headers: headers, responseType: 'text' }) - .toPromise() - .then(response => { - console.debug('ResourceService --> getResource() --> response: ' + response); - return response; - }) - .catch(this.handleError); + return this.http.get(res.href, {headers: headers, responseType: 'text'}) + .toPromise() + .then(response => { + console.debug('ResourceService --> Loading resource ' + res.name + ' :\n' + response); + return response; + }) + .catch(this.handleError); } - saveResource(url: string, xml : string): Promise<string> { - let headers = this.addXmlHeaders(new HttpHeaders()); + saveResource(resource: Resource, content: string): Promise<string> { + let headers = this.addHeaders(new HttpHeaders(), resource.name); headers = this.addCsrfHeaders(headers); - this.logHeaders(headers); + //this.logHeaders(headers); - return this.http.put(url, xml, {headers: headers}) + console.debug('ResourceService --> Persisting ' + resource.name + '\n' + content); + + return this.http.put(resource.href, content, {headers: headers}) .toPromise() - .then(() => xml) + .then(() => content) .catch(this.handleError); } - createResource(name: string, xml : string): Promise<string> { - let headers = this.addXmlHeaders(new HttpHeaders()); + createResource(resType: string, resource: Resource, content : string): Promise<string> { + let headers = this.addHeaders(new HttpHeaders(), resource.name); headers = this.addCsrfHeaders(headers); - this.logHeaders(headers); + //this.logHeaders(headers); - let url = this.topologiesUrl + "/" + name; - return this.http.put(url, xml, {headers: headers}) + let url = ((resType === 'Descriptors') ? this.descriptorsUrl : this.providersUrl) + '/' + name; + return this.http.put(url, content, {headers: headers}) .toPromise() - .then(() => xml) + .then(() => content) .catch(this.handleError); } deleteResource(href: string): Promise<string> { let headers = this.addJsonHeaders(new HttpHeaders()); headers = this.addCsrfHeaders(headers); - this.logHeaders(headers); + //this.logHeaders(headers); return this.http.delete(href, { headers: headers } ) .toPromise() @@ -176,10 +186,17 @@ export class ResourceService { this.selectedResourceSource.next(value); } - changedResource(value: string) { + resourceChanged(value: string) { this.changedResourceSource.next(value); } + providerConfigurationChanged(pc: Array<ProviderConfig>) { + this.changedProviderConfigurationSource.next(pc); + } + + descriptorChanged(desc: Descriptor) { + this.changedDescriptorSource.next(desc); + } public getResourceDisplayName(res: Resource): string { if (res.name) { @@ -196,7 +213,7 @@ export class ResourceService { } private logHeaders(headers: HttpHeaders) { - let debugMsg = 'ResourceService --> Request header count: ' + headers.keys().length + '\n'; + let debugMsg = 'ResourceService --> Request headers:\n'; headers.keys().forEach(key => { debugMsg += ' ' + key + '=' + headers.get(key) + '\n'; }); http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-applications/src/main/resources/applications/admin-ui/app/index.html ---------------------------------------------------------------------- diff --git a/gateway-applications/src/main/resources/applications/admin-ui/app/index.html b/gateway-applications/src/main/resources/applications/admin-ui/app/index.html index 37340a6..b18f391 100644 --- a/gateway-applications/src/main/resources/applications/admin-ui/app/index.html +++ b/gateway-applications/src/main/resources/applications/admin-ui/app/index.html @@ -14,4 +14,4 @@ --><!doctype html><html><head><meta charset="utf-8"><title>Apache Knox Manager</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" type="image/x-icon" href="favicon.ico"><meta name="viewport" content="width=device-width,initial-scale=1"><!-- Latest compiled and minified CSS --><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"><!-- Optional theme --><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous"><!-- Custom styles for this template --><link href="assets/sticky-footer.css" rel="stylesheet"><script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script><!-- Latest compiled and minified JavaScript --><sc ript src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script><script src="assets/vkbeautify.js"></script><!-- <script src="assets/esprima.js"></script> <script src="assets/js-yaml.min.js"></script> - --><link href="styles.2ee5b7f4cd59a6cf015e.bundle.css" rel="stylesheet"/></head><body><div class="navbar-wrapper"><div class="container-fluid"><nav class="navbar navbar-inverse navbar-static-top"><div class="container-fluid"><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"><span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span></button> <a class="navbar-brand" href="#"><img style="max-width:200px; margin-top: -9px;" src="assets/knox-logo-transparent.gif" alt="Apache Knox Manager"></a></div></div></nav></div><!-- Content --><resource-management></resource-management><footer class="footer"><div>Knox Manager Version 0.1.0</div><gateway-version></gateway-version></footer><script type="text/javascript" src="inline.1b8a794b441ffd968489.bundle.js"></script><script type="text/javascript " src="scripts.c50bb762c438ae0f8842.bundle.js"></script><script type="text/javascript" src="main.10e7c17d5707ac382dbb.bundle.js"></script></div></body></html> \ No newline at end of file + --><link href="styles.2ee5b7f4cd59a6cf015e.bundle.css" rel="stylesheet"/></head><body><div class="navbar-wrapper"><div class="container-fluid"><nav class="navbar navbar-inverse navbar-static-top"><div class="container-fluid"><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"><span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span></button> <a class="navbar-brand" href="#"><img style="max-width:200px; margin-top: -9px;" src="assets/knox-logo-transparent.gif" alt="Apache Knox Manager"></a></div></div></nav></div><!-- Content --><resource-management></resource-management><footer class="footer"><div>Knox Manager Version 0.1.0</div><gateway-version></gateway-version></footer><script type="text/javascript" src="inline.1bf9c629c91002a65d30.bundle.js"></script><script type="text/javascript " src="scripts.c50bb762c438ae0f8842.bundle.js"></script><script type="text/javascript" src="main.4689931d0eaf6a8f51c9.bundle.js"></script></div></body></html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-applications/src/main/resources/applications/admin-ui/app/inline.1b8a794b441ffd968489.bundle.js ---------------------------------------------------------------------- diff --git a/gateway-applications/src/main/resources/applications/admin-ui/app/inline.1b8a794b441ffd968489.bundle.js b/gateway-applications/src/main/resources/applications/admin-ui/app/inline.1b8a794b441ffd968489.bundle.js deleted file mode 100644 index d758f83..0000000 --- a/gateway-applications/src/main/resources/applications/admin-ui/app/inline.1b8a794b441ffd968489.bundle.js +++ /dev/null @@ -1 +0,0 @@ -!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,c,u){for(var a,i,f,l=0,s=[];l<r.length;l++)t[i=r[l]]&&s.push(t[i][0]),t[i]=0;for(a in c)Object.prototype.hasOwnProperty.call(c,a)&&(e[a]=c[a]);for(n&&n(r,c,u);s.length;)s.shift()();if(u)for(l=0;l<u.length;l++)f=o(o.s=u[l]);return f};var r={},t={2:0};function o(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,o){n=t[e]=[r,o]});n[2]=r;var c=document.getElementsByTagName("head")[0],u=document.createElement("script");u.type="text/javascript",u.charset="utf-8",u.async=!0,u.timeout=12e4,o.nc&&u.setAttribute("nonce",o.nc),u.src=o.p+""+e+"."+{0:"10e7c17d5707ac382dbb",1:"aed76669724804835353"}[e]+".chunk.js";var a=setTimeout(i,12e4);function i(){u.onerror=u.onload=null,clearTimeout(a);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chu nk "+e+" failed.")),t[e]=void 0)}return u.onerror=u.onload=i,c.appendChild(u),r},o.m=e,o.c=r,o.d=function(e,n,r){o.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="",o.oe=function(e){throw console.error(e),e}}([]); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/knox/blob/cd336759/gateway-applications/src/main/resources/applications/admin-ui/app/inline.1bf9c629c91002a65d30.bundle.js ---------------------------------------------------------------------- diff --git a/gateway-applications/src/main/resources/applications/admin-ui/app/inline.1bf9c629c91002a65d30.bundle.js b/gateway-applications/src/main/resources/applications/admin-ui/app/inline.1bf9c629c91002a65d30.bundle.js new file mode 100644 index 0000000..4fef45c --- /dev/null +++ b/gateway-applications/src/main/resources/applications/admin-ui/app/inline.1bf9c629c91002a65d30.bundle.js @@ -0,0 +1 @@ +!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,c,a){for(var u,i,f,l=0,s=[];l<r.length;l++)t[i=r[l]]&&s.push(t[i][0]),t[i]=0;for(u in c)Object.prototype.hasOwnProperty.call(c,u)&&(e[u]=c[u]);for(n&&n(r,c,a);s.length;)s.shift()();if(a)for(l=0;l<a.length;l++)f=o(o.s=a[l]);return f};var r={},t={2:0};function o(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,o){n=t[e]=[r,o]});n[2]=r;var c=document.getElementsByTagName("head")[0],a=document.createElement("script");a.type="text/javascript",a.charset="utf-8",a.async=!0,a.timeout=12e4,o.nc&&a.setAttribute("nonce",o.nc),a.src=o.p+""+e+"."+{0:"4689931d0eaf6a8f51c9",1:"aed76669724804835353"}[e]+".chunk.js";var u=setTimeout(i,12e4);function i(){a.onerror=a.onload=null,clearTimeout(u);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chu nk "+e+" failed.")),t[e]=void 0)}return a.onerror=a.onload=i,c.appendChild(a),r},o.m=e,o.c=r,o.d=function(e,n,r){o.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="",o.oe=function(e){throw console.error(e),e}}([]); \ No newline at end of file
