Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chaitali/create client #4793

Merged
merged 7 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion components/automate-ui/src/app/entities/clients/client.action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,20 @@ export enum ClientActionTypes {
GET_FAILURE = 'CLIENTS::GET::FAILURE',
DELETE = 'CLIENTS::DELETE',
DELETE_SUCCESS = 'CLIENTS::DELETE::SUCCESS',
DELETE_FAILURE = 'CLIENTS::DELETE::FAILURE'
DELETE_FAILURE = 'CLIENTS::DELETE::FAILURE',
CREATE = 'CLIENTS::CREATE',
CREATE_SUCCESS = 'CLIENTS::CREATE::SUCCESS',
CREATE_FAILURE = 'CLIENTS::CREATE::FAILURE'
}

export interface CreateClientSuccessPayload {
name: string;
client_key: {
name: string,
public_key: string,
expiration_date: string,
private_key: string
};
}

export interface ClientsSuccessPayload {
Expand Down Expand Up @@ -58,6 +71,29 @@ export class GetClientFailure implements Action {
constructor(public payload: HttpErrorResponse) { }
}

export interface CreateClientPayload {
name: string;
validator: boolean;
org_id: string;
server_id: string;
create_key: boolean;
}

export class CreateClient implements Action {
readonly type = ClientActionTypes.CREATE;
constructor(public payload: CreateClientPayload) { }
}

export class CreateClientSuccess implements Action {
readonly type = ClientActionTypes.CREATE_SUCCESS;
constructor(public payload: CreateClientSuccessPayload) { }
}

export class CreateClientFailure implements Action {
readonly type = ClientActionTypes.CREATE_FAILURE;
constructor(public payload: HttpErrorResponse) { }
}

export class DeleteClient implements Action {
readonly type = ClientActionTypes.DELETE;
constructor(public payload: { server_id: string, org_id: string, name: string }) { }
Expand All @@ -80,6 +116,9 @@ export type ClientActions =
| GetClient
| GetClientSuccess
| GetClientFailure
| CreateClient
| CreateClientSuccess
| CreateClientFailure
| DeleteClient
| DeleteClientSuccess
| DeleteClientFailure;
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Injectable } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { of as observableOf } from 'rxjs';
import { catchError, mergeMap, map } from 'rxjs/operators';
import { catchError, mergeMap, map, filter } from 'rxjs/operators';
import { CreateNotification } from 'app/entities/notifications/notification.actions';
import { Type } from 'app/entities/notifications/notification.model';
import { HttpStatus } from 'app/types/types';

import {
GetClients,
Expand All @@ -15,6 +16,10 @@ import {
GetClient,
GetClientSuccess,
GetClientFailure,
CreateClient,
CreateClientSuccess,
CreateClientSuccessPayload,
CreateClientFailure,
DeleteClient,
DeleteClientSuccess,
DeleteClientFailure
Expand Down Expand Up @@ -69,6 +74,31 @@ export class ClientEffects {
});
}));

@Effect()
createClient$ = this.actions$.pipe(
ofType(ClientActionTypes.CREATE),
mergeMap((action: CreateClient) =>
this.requests.createClient(action.payload).pipe(
map((resp: CreateClientSuccessPayload) => new CreateClientSuccess(resp)),
catchError((error: HttpErrorResponse) => observableOf(new CreateClientFailure(error))))));

@Effect()
createClientSuccess$ = this.actions$.pipe(
ofType(ClientActionTypes.CREATE_SUCCESS),
map(({ payload: { name } }: CreateClientSuccess) => new CreateNotification({
type: Type.info,
message: `Created client ${name}`
})));

@Effect()
createClientFailure$ = this.actions$.pipe(
ofType(ClientActionTypes.CREATE_FAILURE),
filter(({ payload }: CreateClientFailure) => payload.status !== HttpStatus.CONFLICT),
map(({ payload }: CreateClientFailure) => new CreateNotification({
type: Type.error,
message: `Could not create client: ${payload.error.error || payload}`
})));

@Effect()
deleteClient$ = this.actions$.pipe(
ofType(ClientActionTypes.DELETE),
Expand Down Expand Up @@ -98,4 +128,5 @@ export class ClientEffects {
message: `Could not delete client: ${msg || error}`
});
}));

}
44 changes: 37 additions & 7 deletions components/automate-ui/src/app/entities/clients/client.reducer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
import { set, pipe } from 'lodash/fp';
import { set, pipe, unset } from 'lodash/fp';
import { EntityStatus } from 'app/entities/entities';
import { ClientActionTypes, ClientActions } from './client.action';
import { Client } from './client.model';
import { HttpErrorResponse } from '@angular/common/http';

export interface ClientEntityState extends EntityState<Client> {
clientsStatus: EntityStatus;
Expand All @@ -12,21 +13,29 @@ export interface ClientEntityState extends EntityState<Client> {
total: number
};
deleteStatus: EntityStatus;
saveStatus: EntityStatus;
saveError: HttpErrorResponse;
createClient: {
client_key: Object,
name: string
};
}

const GET_ALL_STATUS = 'getAllStatus';
const DELETE_STATUS = 'deleteStatus';
const SAVE_STATUS = 'saveStatus';
const SAVE_ERROR = 'saveError';

export const clientEntityAdapter: EntityAdapter<Client> =
createEntityAdapter<Client>({
selectId: (client: Client) => client.name
selectId: (client: Client) => client.name
});

export const ClientEntityInitialState: ClientEntityState =
clientEntityAdapter.getInitialState(<ClientEntityState>{
getAllStatus: EntityStatus.notLoaded,
deleteStatus: EntityStatus.notLoaded
});
getAllStatus: EntityStatus.notLoaded,
deleteStatus: EntityStatus.notLoaded
});

export function clientEntityReducer(
state: ClientEntityState = ClientEntityInitialState,
Expand All @@ -38,13 +47,34 @@ export function clientEntityReducer(

case ClientActionTypes.GET_ALL_SUCCESS:
return pipe(
set(GET_ALL_STATUS, EntityStatus.loadingSuccess),
set('clientList.items', action.payload.clients || []),
set('clientList.total', action.payload.total || 0)
)(state) as ClientEntityState;
)(state) as ClientEntityState;

case ClientActionTypes.GET_ALL_FAILURE:
return set(GET_ALL_STATUS, EntityStatus.loadingFailure, state);

case ClientActionTypes.CREATE: {
return set(SAVE_STATUS, EntityStatus.loading, state) as ClientEntityState;
}

case ClientActionTypes.CREATE_SUCCESS: {
return pipe(
unset(SAVE_ERROR),
set(SAVE_STATUS, EntityStatus.loadingSuccess),
set('createClient.client_key', action.payload.client_key || []),
set('createClient.name', action.payload.name || '')
)(state) as ClientEntityState;
}

case ClientActionTypes.CREATE_FAILURE: {
return pipe(
set(SAVE_ERROR, action.payload.error),
set(SAVE_STATUS, EntityStatus.loadingFailure)
)(state) as ClientEntityState;
}

case ClientActionTypes.DELETE:
return set(DELETE_STATUS, EntityStatus.loading, state);

Expand All @@ -55,7 +85,7 @@ export function clientEntityReducer(
return pipe(
set(DELETE_STATUS, EntityStatus.loadingSuccess),
set('clientList.items', clients || []),
set('clientList.total', total || 0 )
set('clientList.total', total || 0)
)(state) as ClientEntityState;

case ClientActionTypes.DELETE_FAILURE:
Expand Down
19 changes: 17 additions & 2 deletions components/automate-ui/src/app/entities/clients/client.requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment as env } from 'environments/environment';
import { ClientsPayload } from './client.action';
import { ClientsPayload, CreateClientPayload } from './client.action';
import { Client } from './client.model';
import { InterceptorSkipHeader } from 'app/services/http/http-client-auth.interceptor';

Expand All @@ -13,6 +13,16 @@ export interface ClientsResponse {
total: number;
}

export interface CreateClientResponse {
name: string;
client_key: {
name: string,
public_key: string,
expiration_date: string,
private_key: string
};
}

@Injectable()
export class ClientRequests {

Expand All @@ -36,9 +46,14 @@ export class ClientRequests {
`${env.infra_proxy_url}/servers/${server_id}/orgs/${org_id}/clients/${name}`, {headers});
}

public createClient(payload: CreateClientPayload): Observable<CreateClientResponse> {
return this.http.post<CreateClientResponse>(
`${env.infra_proxy_url}/servers/${payload.server_id}/orgs/${payload.org_id}/clients`,
payload);
}

public deleteClient(server_id: string, org_id: string, name: string): Observable<{}> {
return this.http.delete(`${env.infra_proxy_url}/servers/${server_id}/orgs/${org_id}/clients/${name}`,
{headers});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,18 @@ export const clientList = createSelector(
clientState,
(state) => state.clientList
);

export const saveStatus = createSelector(
clientState,
(state) => state.saveStatus
);

export const saveError = createSelector(
clientState,
(state) => state.saveError
);

export const createClient = createSelector(
clientState,
(state) => state.createClient
);
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<section class="clients">
<app-create-client-modal [openEvent]="openNotificationModal"
[serverId]="serverId" [orgId]="orgId"></app-create-client-modal>

<chef-loading-spinner *ngIf="clientsListLoading" size="50"></chef-loading-spinner>
<app-delete-infra-object-modal
[visible]="deleteModalVisible"
Expand All @@ -13,6 +16,7 @@
<div class="search-items">
<app-infra-search-bar (searchButtonClick)="searchClients($event)" placeHolder="Clients">
</app-infra-search-bar>
<chef-button primary (click)="openCreateClientModal()">Create Client</chef-button>
</div>
<chef-loading-spinner class="full-screen-spinner" *ngIf="searching" size="50" fixed></chef-loading-spinner>
<div class="empty-section" *ngIf="!searching && !clients.length">
Expand All @@ -33,7 +37,7 @@
<a [routerLink]="['/infrastructure','chef-servers', serverId, 'organizations', orgId, 'clients', client.name]">{{ client.name }}</a>
</chef-td>
<chef-td class="three-dot-column">
<mat-select panelClass="chef-control-menu" id="menu-{{client.id}}">
<mat-select panelClass="chef-control-menu">
<mat-option data-cy="delete" (onSelectionChange)="startClientDelete(client)">Delete</mat-option>
</mat-select>
</chef-td>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ chef-loading-spinner {
background-color: $chef-white;
opacity: 0.7;
width: initial;
height: 100%;
}

.empty-section {
Expand All @@ -60,3 +61,26 @@ img {
.three-dot-column {
text-align: right;
}

chef-button button {
line-height: 20px;
letter-spacing: 0.5px;
padding: 8px 16px;

&:focus {
outline: none;
}

}

.clients-list-paging {
margin: 0 0 35px;
}

::ng-deep {
.search-items app-infra-search-bar .search-wrapper {
width: 87%;
float: left;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('ClientsComponent', () => {
MockComponent({ selector: 'input', inputs: ['resetOrigin'] }),
MockComponent({ selector: 'mat-select' }),
MockComponent({ selector: 'mat-option' }),
MockComponent({ selector: 'app-create-client-modal', inputs: ['openEvent'] }),
ClientsComponent
],
providers: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ import { NgrxStateAtom } from 'app/ngrx.reducers';
import { LayoutFacadeService, Sidebar } from 'app/entities/layout/layout.facade';
import { GetClients, DeleteClient } from 'app/entities/clients/client.action';
import { Client } from 'app/entities/clients/client.model';
import {
getAllStatus,
clientList
} from 'app/entities/clients/client.selectors';

import { getAllStatus, clientList } from 'app/entities/clients/client.selectors';

@Component({
selector: 'app-clients',
Expand All @@ -38,12 +34,12 @@ export class ClientsComponent implements OnInit, OnDestroy {
public clientToDelete: Client;
public deleteModalVisible = false;
private isDestroyed = new Subject<boolean>();

public openNotificationModal = new EventEmitter<void>();

constructor(
private store: Store<NgrxStateAtom>,
private layoutFacade: LayoutFacadeService
) { }
) {}

ngOnInit() {
this.layoutFacade.showSidebar(Sidebar.Infrastructure);
Expand Down Expand Up @@ -103,6 +99,10 @@ export class ClientsComponent implements OnInit, OnDestroy {
this.store.dispatch(new GetClients(payload));
}

openCreateClientModal() {
this.openNotificationModal.emit();
}

resetKeyTabRedirection(resetLink: boolean) {
this.resetKeyRedirection.emit(resetLink);
}
Expand Down
Loading