From 6956ad197c81659ac9f97b779347a4e0a482d792 Mon Sep 17 00:00:00 2001 From: vinay sharma Date: Fri, 24 Dec 2021 18:19:51 +0530 Subject: [PATCH] Server details page - UI should have the option to update the WebUI key (#6440) * added changes to update the webui key on server details page Signed-off-by: Vinay Sharma * added some minor changes Signed-off-by: Vinay Sharma --- .../app/entities/servers/server.actions.ts | 15 ++-- .../app/entities/servers/server.effects.ts | 11 ++- .../src/app/entities/servers/server.model.ts | 4 +- .../app/entities/servers/server.requests.ts | 6 +- .../chef-server-details.component.html | 12 ++- .../chef-server-details.component.ts | 41 ++++++++- .../update-web-uikey-slider.component.html | 18 ++-- .../update-web-uikey-slider.component.spec.ts | 34 +++---- .../update-web-uikey-slider.component.ts | 89 +++---------------- .../infra-proxy/chef-server-details.spec.ts | 13 ++- 10 files changed, 114 insertions(+), 129 deletions(-) diff --git a/components/automate-ui/src/app/entities/servers/server.actions.ts b/components/automate-ui/src/app/entities/servers/server.actions.ts index 422bf4f5535..98ae1212bc5 100644 --- a/components/automate-ui/src/app/entities/servers/server.actions.ts +++ b/components/automate-ui/src/app/entities/servers/server.actions.ts @@ -48,6 +48,7 @@ export class GetServersSuccess implements Action { export class GetServersFailure implements Action { readonly type = ServerActionTypes.GET_ALL_FAILURE; + constructor(public payload: HttpErrorResponse) { } } @@ -79,26 +80,31 @@ export interface CreateServerPayload { export class CreateServer implements Action { readonly type = ServerActionTypes.CREATE; + constructor(public payload: CreateServerPayload) { } } export class CreateServerSuccess implements Action { readonly type = ServerActionTypes.CREATE_SUCCESS; + constructor(public payload: ServerSuccessPayload) { } } export class CreateServerFailure implements Action { readonly type = ServerActionTypes.CREATE_FAILURE; + constructor(public payload: HttpErrorResponse) { } } export class DeleteServer implements Action { readonly type = ServerActionTypes.DELETE; + constructor(public payload: { id: string, name: string }) { } } export class DeleteServerSuccess implements Action { readonly type = ServerActionTypes.DELETE_SUCCESS; + constructor(public payload: { id: string, name: string }) { } } @@ -136,17 +142,14 @@ export class GetUsers implements Action { export class GetUsersSuccess implements Action { readonly type = ServerActionTypes.GET_USERS_SUCCESS; + constructor(public payload: UsersSuccessPayload) { } } export class GetUsersFailure implements Action { readonly type = ServerActionTypes.GET_USERS_FAILURE; - constructor(public payload: HttpErrorResponse) { } -} -export interface WebUIKeyPayload { - server_id: any; - key: string; + constructor(public payload: HttpErrorResponse) { } } export class UpdateWebUIKey implements Action { @@ -158,7 +161,7 @@ export class UpdateWebUIKey implements Action { export class UpdateWebUIKeySuccess implements Action { readonly type = ServerActionTypes.UPDATE_WEB_UI_KEY_SUCCESS; - constructor(public payload: WebUIKeyPayload) { } + constructor(public payload: WebUIKey) { } } export class UpdateWebUIKeyFailure implements Action { diff --git a/components/automate-ui/src/app/entities/servers/server.effects.ts b/components/automate-ui/src/app/entities/servers/server.effects.ts index aaa5bdfb21e..9661c91fb2d 100644 --- a/components/automate-ui/src/app/entities/servers/server.effects.ts +++ b/components/automate-ui/src/app/entities/servers/server.effects.ts @@ -32,8 +32,7 @@ import { UsersSuccessPayload, UpdateWebUIKey, UpdateWebUIKeySuccess, - UpdateWebUIKeyFailure, - WebUIKeyPayload + UpdateWebUIKeyFailure } from './server.actions'; import { @@ -195,17 +194,17 @@ export class ServerEffects { UpdateWebUIKey$ = createEffect(() => this.actions$.pipe( ofType(ServerActionTypes.UPDATE_WEB_UI_KEY), - mergeMap(({ payload}: UpdateWebUIKey) => + mergeMap(({ payload }: UpdateWebUIKey) => this.requests.updateWebUIKey(payload).pipe( - map((resp: WebUIKeyPayload) => new UpdateWebUIKeySuccess(resp)), + map((resp) => new UpdateWebUIKeySuccess(resp)), catchError((error: HttpErrorResponse) => observableOf(new UpdateWebUIKeyFailure(error))))))); UpdateWebUIKeySuccess$ = createEffect(() => this.actions$.pipe( ofType(ServerActionTypes.UPDATE_WEB_UI_KEY_SUCCESS), - map(({ payload: webuikey }: UpdateWebUIKeySuccess) => new CreateNotification({ + map(() => new CreateNotification({ type: Type.info, - message: `Successfully updated Web UI Key ${webuikey.server_id}.` + message: 'Successfully updated Web UI Key.' })))); UpdateWebUIKeyFailure$ = createEffect(() => this.actions$.pipe( diff --git a/components/automate-ui/src/app/entities/servers/server.model.ts b/components/automate-ui/src/app/entities/servers/server.model.ts index c2a45525bf9..106b725a289 100644 --- a/components/automate-ui/src/app/entities/servers/server.model.ts +++ b/components/automate-ui/src/app/entities/servers/server.model.ts @@ -18,6 +18,6 @@ export interface User { } export interface WebUIKey { - server_id: string; - key: string; + id: string; + webui_key: string; } diff --git a/components/automate-ui/src/app/entities/servers/server.requests.ts b/components/automate-ui/src/app/entities/servers/server.requests.ts index 24ee53779a9..421fe294982 100644 --- a/components/automate-ui/src/app/entities/servers/server.requests.ts +++ b/components/automate-ui/src/app/entities/servers/server.requests.ts @@ -5,7 +5,7 @@ import { mapKeys, snakeCase } from 'lodash/fp'; import { environment as env } from 'environments/environment'; import { Server, User, WebUIKey } from './server.model'; -import { CreateServerPayload, ServerSuccessPayload, WebUIKeyPayload } from './server.actions'; +import { CreateServerPayload, ServerSuccessPayload } from './server.actions'; export interface ServersResponse { servers: Server[]; @@ -52,7 +52,7 @@ export class ServerRequests { // Need to change API endpoint for update WebUIKey public updateWebUIKey(payload): Observable { - return this.http.put - (`${env.infra_proxy_url}/servers/${payload.server_id}`, payload.key); + return this.http.post + (`${env.infra_proxy_url}/servers/update`, payload); } } diff --git a/components/automate-ui/src/app/modules/infra-proxy/chef-server-details/chef-server-details.component.html b/components/automate-ui/src/app/modules/infra-proxy/chef-server-details/chef-server-details.component.html index 59874a9f382..ec52424c5c2 100644 --- a/components/automate-ui/src/app/modules/infra-proxy/chef-server-details/chef-server-details.component.html +++ b/components/automate-ui/src/app/modules/infra-proxy/chef-server-details/chef-server-details.component.html @@ -27,7 +27,8 @@ warning Invalid - Update @@ -226,8 +227,13 @@

No Organization available.

- + diff --git a/components/automate-ui/src/app/modules/infra-proxy/chef-server-details/chef-server-details.component.ts b/components/automate-ui/src/app/modules/infra-proxy/chef-server-details/chef-server-details.component.ts index e96d637f4c0..012a0f32e95 100644 --- a/components/automate-ui/src/app/modules/infra-proxy/chef-server-details/chef-server-details.component.ts +++ b/components/automate-ui/src/app/modules/infra-proxy/chef-server-details/chef-server-details.component.ts @@ -13,11 +13,11 @@ import { Regex } from 'app/helpers/auth/regex'; import { LayoutFacadeService, Sidebar } from 'app/entities/layout/layout.facade'; import { pending, EntityStatus, allLoaded } from 'app/entities/entities'; import { - getStatus, serverFromRoute, updateStatus, getUsers, getUsersStatus + getStatus, serverFromRoute, updateStatus, getUsers, getUsersStatus, updateWebUIKey } from 'app/entities/servers/server.selectors'; -import { Server } from 'app/entities/servers/server.model'; -import { GetServer, UpdateServer +import { Server, WebUIKey } from 'app/entities/servers/server.model'; +import { GetServer, UpdateServer, UpdateWebUIKey // , GetUsers } from 'app/entities/servers/server.actions'; import { GetOrgs, CreateOrg, DeleteOrg } from 'app/entities/orgs/org.actions'; @@ -68,6 +68,12 @@ export class ChefServerDetailsComponent implements OnInit, OnDestroy { public authFailure = false; public isValid = false; + + public updateWebuiKeyForm: FormGroup; + public updatingWebuiKey = false; + public webuiKey: WebUIKey; + public updateWebUIKeySuccessful = false; + constructor( private fb: FormBuilder, private store: Store, @@ -84,6 +90,10 @@ export class ChefServerDetailsComponent implements OnInit, OnDestroy { admin_key: ['', [Validators.required]], projects: [[]] }); + + this.updateWebuiKeyForm = this.fb.group({ + webUiKey: ['', [Validators.required]] + }); } ngOnInit() { @@ -220,6 +230,17 @@ export class ChefServerDetailsComponent implements OnInit, OnDestroy { this.isUserLoaded = false; } }); + + this.store.select(updateWebUIKey).pipe( + takeUntil(this.isDestroyed), + filter(state => this.updatingWebuiKey && !pending(state))) + .subscribe((state) => { + this.updatingWebuiKey = false; + this.updateWebUIKeySuccessful = (state === EntityStatus.loadingSuccess); + if (this.updateWebUIKeySuccessful) { + this.isValid = true; + } + }); } ngOnDestroy(): void { @@ -292,4 +313,18 @@ export class ChefServerDetailsComponent implements OnInit, OnDestroy { }; this.store.dispatch(new UpdateServer({server: updatedServer})); } + + + public updateWebuiKey(): void { + this.updatingWebuiKey = true; + this.webuiKey = { + id: this.id, + webui_key: this.updateWebuiKeyForm.controls['webUiKey'].value + }; + this.updatingWebuiKeyData(this.webuiKey); + this.updateWebuiKeyForm.reset(); + } + private updatingWebuiKeyData(webuikey: WebUIKey) { + this.store.dispatch(new UpdateWebUIKey(webuikey)); + } } diff --git a/components/automate-ui/src/app/modules/infra-proxy/update-web-uikey-slider/update-web-uikey-slider.component.html b/components/automate-ui/src/app/modules/infra-proxy/update-web-uikey-slider/update-web-uikey-slider.component.html index 6046e962d92..1c9aca62b7f 100644 --- a/components/automate-ui/src/app/modules/infra-proxy/update-web-uikey-slider/update-web-uikey-slider.component.html +++ b/components/automate-ui/src/app/modules/infra-proxy/update-web-uikey-slider/update-web-uikey-slider.component.html @@ -8,7 +8,7 @@

Update Web UI Key

close -
+
@@ -22,11 +22,11 @@

Update Web UI Key

type="text" autocomplete="off" (keyup)="handleInput($event)" - data-cy="enter-key"> + data-cy="enter-webui-key"> + *ngIf="updateWebuiKeyForm.get('webUiKey').hasError('required') && updateWebuiKeyForm.get('webUiKey').dirty"> WEB UI KEY is required.
@@ -35,13 +35,13 @@

Update Web UI Key

Cancel - - - Upload Web UI Key - Uploading Web UI Key + (click)="updateWebUIkey()"> + + Upload Web UI Key + Uploading Web UI Key
diff --git a/components/automate-ui/src/app/modules/infra-proxy/update-web-uikey-slider/update-web-uikey-slider.component.spec.ts b/components/automate-ui/src/app/modules/infra-proxy/update-web-uikey-slider/update-web-uikey-slider.component.spec.ts index c69d7590011..308e17d7e15 100644 --- a/components/automate-ui/src/app/modules/infra-proxy/update-web-uikey-slider/update-web-uikey-slider.component.spec.ts +++ b/components/automate-ui/src/app/modules/infra-proxy/update-web-uikey-slider/update-web-uikey-slider.component.spec.ts @@ -9,7 +9,7 @@ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { Regex } from 'app/helpers/auth/regex'; import { using } from 'app/testing/spec-helpers'; import { WebUIKey } from 'app/entities/servers/server.model'; -import { UpdateWebUIKeyFailure, UpdateWebUIKeySuccess, WebUIKeyPayload } from 'app/entities/servers/server.actions'; +import { UpdateWebUIKeyFailure, UpdateWebUIKeySuccess } from 'app/entities/servers/server.actions'; import { HttpStatus } from 'app/types/types'; import { HttpErrorResponse } from '@angular/common/http'; @@ -46,11 +46,11 @@ describe('UpdateWebUIKeySliderComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(UpdateWebUIKeySliderComponent); component = fixture.componentInstance; - component.updateKeyForm = new FormBuilder().group({ + component.updateWebuiKeyForm = new FormBuilder().group({ webuikey: ['', [Validators.required, Validators.pattern(Regex.patterns.NON_BLANK), Validators.pattern(Regex.patterns.NO_WILDCARD_ALLOW_HYPHEN)]] }); - updateKeyForm = component.updateKeyForm; + updateKeyForm = component.updateWebuiKeyForm; fixture.detectChanges(); }); @@ -71,6 +71,7 @@ describe('UpdateWebUIKeySliderComponent', () => { expect(updateKeyForm.valid).toBeFalsy(); expect(errors['required']).toBeTruthy(); }); + }); describe('the form should be valid', () => { it('when all inputs are filled and valid', () => { @@ -101,13 +102,8 @@ describe('UpdateWebUIKeySliderComponent', () => { describe('#webuikey', () => { let store: Store; const webuikey: WebUIKey = { - server_id: 'test_server', - key: 'test_webuikey' - }; - - const key: WebUIKeyPayload = { - server_id: 'test_server', - key: 'test_webuikey' + id: 'test_server', + webui_key: 'test_webuikey' }; beforeEach(() => { @@ -120,25 +116,24 @@ describe('UpdateWebUIKeySliderComponent', () => { }); it('should be invalid when no fields are filled out', () => { - expect(component.updateKeyForm.valid).toBeFalsy(); + expect(component.updateWebuiKeyForm.valid).toBeFalsy(); }); it('should be valid when all fields are filled out', () => { - component.updateKeyForm.controls['webuikey'].setValue(webuikey.key); - expect(component.updateKeyForm.valid).toBeTruthy(); + component.updateWebuiKeyForm.controls['webuikey'].setValue(webuikey.webui_key); + expect(component.updateWebuiKeyForm.valid).toBeTruthy(); }); it('hide slider after updating webuikey.', () => { - component.updateKeyForm.controls['webuikey'].setValue(webuikey.key); - component.onSubmit(); + component.updateWebuiKeyForm.controls['webuikey'].setValue(webuikey.webui_key); + component.updateWebUIkey(); - store.dispatch(new UpdateWebUIKeySuccess( - { server_id: key.server_id, key: key.key})); + store.dispatch(new UpdateWebUIKeySuccess(webuikey)); }); it('on create , slider is closed with failure banner', () => { - component.updateKeyForm.controls['webuikey'].setValue(webuikey.key); - component.onSubmit(); + component.updateWebuiKeyForm.controls['webuikey'].setValue(webuikey.webui_key); + component.updateWebUIkey(); const error = { status: HttpStatus.INTERNAL_SERVER_ERROR, @@ -148,5 +143,4 @@ describe('UpdateWebUIKeySliderComponent', () => { expect(component.conflictError).toBe(false); }); }); - }); }); diff --git a/components/automate-ui/src/app/modules/infra-proxy/update-web-uikey-slider/update-web-uikey-slider.component.ts b/components/automate-ui/src/app/modules/infra-proxy/update-web-uikey-slider/update-web-uikey-slider.component.ts index 4469bdeb177..48123594780 100644 --- a/components/automate-ui/src/app/modules/infra-proxy/update-web-uikey-slider/update-web-uikey-slider.component.ts +++ b/components/automate-ui/src/app/modules/infra-proxy/update-web-uikey-slider/update-web-uikey-slider.component.ts @@ -1,91 +1,37 @@ -import { Component, HostBinding, EventEmitter, Input, OnInit, OnDestroy } from '@angular/core'; -import { FormGroup, FormBuilder, Validators } from '@angular/forms'; -import { Regex } from 'app/helpers/auth/regex'; +import { Component, HostBinding, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormGroup } from '@angular/forms'; import { Utilities } from 'app/helpers/utilities/utilities'; -import { NgrxStateAtom } from 'app/ngrx.reducers'; -import { Store } from '@ngrx/store'; -// import { UpdateWebUIKey } from 'app/entities/servers/server.actions'; -import { saveError, updateWebUIKey } from 'app/entities/servers/server.selectors'; -import { Subject, combineLatest } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { isNil } from 'lodash/fp'; @Component({ selector: 'app-update-web-uikey-slider', templateUrl: './update-web-uikey-slider.component.html', styleUrls: ['./update-web-uikey-slider.component.scss'] }) -export class UpdateWebUIKeySliderComponent implements OnInit, OnDestroy { +export class UpdateWebUIKeySliderComponent implements OnInit { @HostBinding('class.active') isSlideOpen = false; - @Input() serverId: string; + @Output() updateWebuiKeyClicked = new EventEmitter(); + @Input() updateWebuiKeyForm: FormGroup; + @Input() updatingWebuiKey = false; + @Input() conflictErrorEvent: EventEmitter; - public server: string; public conflictError = false; - public updated = false; - public updateKeyForm: FormGroup; - public updating = false; - public checkedValidator = false; - public ui_key: any; - public updatedKey: string; - public error: string; - private isDestroyed = new Subject(); - public conflictErrorEvent = new EventEmitter(); - - constructor( - private formBuilder: FormBuilder, - private store: Store - ) { - this.updateKeyForm = this.formBuilder.group({ - webUiKey: ['', [Validators.required, - Validators.pattern(Regex.patterns.NO_WILDCARD_ALLOW_HYPHEN)]] - }); - } ngOnInit() { - this.server = this.serverId; - this.error = ''; - - combineLatest([ - this.store.select(saveError), - this.store.select(updateWebUIKey) - ]).pipe( - takeUntil(this.isDestroyed)) - .subscribe(([ errorSt, webKeyState]) => { - if ( !isNil(errorSt) ) { - this.updated = false; - this.updating = false; - this.error = errorSt?.message; - } else { - this.updating = false; - this.updated = true; - this.updatedKey = webKeyState; - } + this.conflictErrorEvent.subscribe((isConflict: boolean) => { + this.conflictError = isConflict; }); } - ngOnDestroy(): void { - this.isDestroyed.next(true); - this.isDestroyed.complete(); - } - public handleInput(event: KeyboardEvent): void { if (Utilities.isNavigationKey(event)) { return; } - this.conflictError = false; } - uploadWebUIKey() { - this.updating = true; - const webuikey = { - key: this.updateKeyForm.controls['webUiKey'].value, - server_id: this.serverId - }; - console.log('Your form data : ', this.updateKeyForm.value, webuikey ); - // this.store.dispatch(new UpdateWebUIKey(webuikey)); - this.updateKeyForm.reset(); + updateWebUIkey(): void { this.toggleSlide(); -} + this.updateWebuiKeyClicked.emit(); + } toggleSlide() { this.isSlideOpen = !this.isSlideOpen; @@ -95,16 +41,7 @@ export class UpdateWebUIKeySliderComponent implements OnInit, OnDestroy { this.isSlideOpen = true; } - closeUpdateModal(): void { - this.resetUpdateModal(); + closeUpdateModal() { this.toggleSlide(); } - - private resetUpdateModal(): void { - this.updating = false; - this.updated = false; - this.updateKeyForm.reset(); - this.checkedValidator = false; - this.conflictErrorEvent.emit(false); - } } diff --git a/e2e/cypress/integration/ui/infra-proxy/chef-server-details.spec.ts b/e2e/cypress/integration/ui/infra-proxy/chef-server-details.spec.ts index f430a6770d0..d536d31f902 100644 --- a/e2e/cypress/integration/ui/infra-proxy/chef-server-details.spec.ts +++ b/e2e/cypress/integration/ui/infra-proxy/chef-server-details.spec.ts @@ -15,6 +15,7 @@ describe('chef server details', () => { // using dummy admin key value for creating the org const adminKey = 'Dummy--admin--key'; + const webui_key = 'Dummy--webui-key'; before(() => { cy.adminLogin('/').then(() => { @@ -29,7 +30,8 @@ describe('chef server details', () => { id: serverID, name: serverName, fqdn: serverFQDN, - ip_address: serverIP + ip_address: serverIP, + webui_key: webui_key } }); @@ -56,6 +58,15 @@ describe('chef server details', () => { cy.get('[data-cy=add-org-button]').contains('Add Chef Organization'); }); + it('it can update the webui key', () => { + cy.get('[data-cy=update-web-ui-key]').contains('Update').click(); + cy.get('app-chef-server-details .sidenav-header').should('exist'); + cy.get('[data-cy=enter-webui-key]').type(webui_key); + + cy.get('[data-cy=update-webui-key-button]').click(); + cy.get('app-chef-server-details .sidenav-header').should('not.be.visible'); + }); + it('can check empty org list', () => { cy.get('#org-table-container chef-th').should('not.be.visible'); cy.get('[data-cy=empty-list]').should('be.visible');