Skip to content

Commit

Permalink
Stalwart 32 upload slider functionality (#6654)
Browse files Browse the repository at this point in the history
* Added upload slider

Signed-off-by: Chaitali Mane <cmane@progress.com>

* Update upload functionality

Signed-off-by: Chaitali Mane <cmane@progress.com>

* Added drag and drop function

Signed-off-by: Chaitali Mane <cmane@progress.com>

* Added API integration code

Signed-off-by: Chaitali Mane <cmane@progress.com>

* Added file upload changes

Signed-off-by: Chaitali Mane <cmane@progress.com>

* Added cypress test cases

Signed-off-by: Chaitali Mane <cmane@progress.com>

* Added unit tests

Signed-off-by: Chaitali Mane <cmane@progress.com>

* Added loader

Signed-off-by: Chaitali Mane <cmane@progress.com>

* Updated code

Signed-off-by: Chaitali Mane <cmane@progress.com>

* added header changes + added some type changes + fixed slider open and close after upload

Signed-off-by: Vinay Sharma <vsharma@chef.io>

* Updated for selector

Signed-off-by: Chaitali Mane <cmane@progress.com>

Co-authored-by: Vinay Sharma <vsharma@chef.io>
  • Loading branch information
2 people authored and kalroy committed Feb 3, 2022
1 parent a095a8e commit a0b2375
Show file tree
Hide file tree
Showing 28 changed files with 777 additions and 17 deletions.
35 changes: 32 additions & 3 deletions components/automate-ui/src/app/entities/orgs/org.actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Action } from '@ngrx/store';

import { Org } from './org.model';
import { Org, UploadFile } from './org.model';

export enum OrgActionTypes {
GET_ALL = 'ORGS::GET_ALL',
Expand All @@ -18,7 +18,10 @@ export enum OrgActionTypes {
DELETE_FAILURE = 'ORGS::DELETE::FAILURE',
UPDATE = 'ORGS::UPDATE',
UPDATE_SUCCESS = 'ORGS::UPDATE::SUCCESS',
UPDATE_FAILURE = 'ORGS::UPDATE::FAILURE'
UPDATE_FAILURE = 'ORGS::UPDATE::FAILURE',
UPLOAD = 'ORGS::UPLOAD',
UPLOAD_SUCCESS = 'ORGS::UPLOAD::SUCCESS',
UPLOAD_FAILURE = 'ORGS::UPLOAD::FAILURE'
}

export interface OrgSuccessPayload {
Expand Down Expand Up @@ -124,6 +127,29 @@ export class UpdateOrgFailure implements Action {
constructor(public payload: HttpErrorResponse) { }
}

export interface UploadSuccessPayload {
success: boolean;
migrationId: string;
}

export class UploadZip implements Action {
readonly type = OrgActionTypes.UPLOAD;

constructor(public payload: UploadFile) { }
}

export class UploadZipSuccess implements Action {
readonly type = OrgActionTypes.UPLOAD_SUCCESS;

constructor(public payload: UploadSuccessPayload) { }
}

export class UploadZipFailure implements Action {
readonly type = OrgActionTypes.UPLOAD_FAILURE;

constructor(public payload: HttpErrorResponse) { }
}

export type OrgActions =
| GetOrgs
| GetOrgsSuccess
Expand All @@ -139,4 +165,7 @@ export type OrgActions =
| DeleteOrgFailure
| UpdateOrg
| UpdateOrgSuccess
| UpdateOrgFailure;
| UpdateOrgFailure
| UploadZip
| UploadZipSuccess
| UploadZipFailure;
32 changes: 32 additions & 0 deletions components/automate-ui/src/app/entities/orgs/org.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import {
UpdateOrgFailure,
UpdateOrgSuccess,
OrgSuccessPayload,
UploadZip,
UploadZipSuccess,
UploadZipFailure,
UploadSuccessPayload,
OrgActionTypes
} from './org.actions';

Expand Down Expand Up @@ -161,4 +165,32 @@ export class OrgEffects {
});
})));

uploadZip$ = createEffect(() =>
this.actions$.pipe(
ofType(OrgActionTypes.UPLOAD),
mergeMap(({ payload: { formData } }: UploadZip) =>
this.requests.uploadZip(formData).pipe(
map((resp: UploadSuccessPayload) => new UploadZipSuccess(resp)),
catchError((error: HttpErrorResponse) =>
observableOf(new UploadZipFailure(error)))))));

uploadZipSuccess$ = createEffect(() =>
this.actions$.pipe(
ofType(OrgActionTypes.UPLOAD_SUCCESS),
map((_UploadZipSuccess) => new CreateNotification({
type: Type.info,
message: 'Successfully uploaded file.'
}))));

uploadZipFailure$ = createEffect(() =>
this.actions$.pipe(
ofType(OrgActionTypes.UPLOAD_FAILURE),
map(({ payload }: UploadZipFailure) => {
const msg = payload.error.error;
return new CreateNotification({
type: Type.error,
message: `Could not upload file: ${msg || payload.error}`
});
})));

}
4 changes: 4 additions & 0 deletions components/automate-ui/src/app/entities/orgs/org.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ export interface Org {
admin_user: string;
projects?: string[];
}

export interface UploadFile {
formData: FormData;
}
22 changes: 20 additions & 2 deletions components/automate-ui/src/app/entities/orgs/org.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { HttpErrorResponse } from '@angular/common/http';
import { set, pipe, unset } from 'lodash/fp';

import { EntityStatus } from 'app/entities/entities';
import { OrgActionTypes, OrgActions } from './org.actions';
import { OrgActionTypes, OrgActions, UploadSuccessPayload } from './org.actions';
import { Org } from './org.model';

export interface OrgEntityState extends EntityState<Org> {
Expand All @@ -13,6 +13,8 @@ export interface OrgEntityState extends EntityState<Org> {
createError: HttpErrorResponse;
updateStatus: EntityStatus;
deleteStatus: EntityStatus;
uploadStatus: EntityStatus;
uploadDetails: UploadSuccessPayload;
}

const GET_ALL_STATUS = 'getAllStatus';
Expand All @@ -21,6 +23,7 @@ const CREATE_STATUS = 'createStatus';
const CREATE_ERROR = 'createError';
const DELETE_STATUS = 'deleteStatus';
const UPDATE_STATUS = 'updateStatus';
const UPLOAD_STATUS = 'uploadStatus';

export const orgEntityAdapter: EntityAdapter<Org> = createEntityAdapter<Org>();

Expand All @@ -31,7 +34,10 @@ export const OrgEntityInitialState: OrgEntityState =
createStatus: EntityStatus.notLoaded,
createError: null,
deleteStatus: EntityStatus.notLoaded,
updateStatus: EntityStatus.notLoaded
updateStatus: EntityStatus.notLoaded,
uploadStatus: EntityStatus.notLoaded,
uploadDetails: null

});

export function orgEntityReducer(
Expand Down Expand Up @@ -98,6 +104,18 @@ export function orgEntityReducer(
case OrgActionTypes.UPDATE_FAILURE:
return set(UPDATE_STATUS, EntityStatus.loadingFailure, state);

case OrgActionTypes.UPLOAD:
return set(UPLOAD_STATUS, EntityStatus.loading, state);

case OrgActionTypes.UPLOAD_SUCCESS:
return pipe(
set(UPLOAD_STATUS, EntityStatus.loadingSuccess),
set('uploadDetails', action.payload || [])
)(state) as OrgEntityState;

case OrgActionTypes.UPLOAD_FAILURE:
return set(UPLOAD_STATUS, EntityStatus.loadingFailure, state);

default:
return state;
}
Expand Down
11 changes: 9 additions & 2 deletions components/automate-ui/src/app/entities/orgs/org.requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { environment as env } from 'environments/environment';
import { Org } from './org.model';

import {
OrgsSuccessPayload, OrgSuccessPayload, CreateOrgPayload
OrgsSuccessPayload,
OrgSuccessPayload,
CreateOrgPayload,
UploadSuccessPayload
} from './org.actions';

@Injectable()
Expand Down Expand Up @@ -36,4 +38,9 @@ export class OrgRequests {
return this.http.put<OrgSuccessPayload>(
`${env.infra_proxy_url}/servers/${org.server_id}/orgs/${org.id}`, org);
}

public uploadZip(formData: FormData): Observable<UploadSuccessPayload> {
return this.http.post<UploadSuccessPayload>
(`${env.infra_proxy_url}/servers/migrations/upload`, formData);
}
}
10 changes: 10 additions & 0 deletions components/automate-ui/src/app/entities/orgs/org.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,13 @@ export const orgFromRoute = createSelector(
routeParams,
(state, { 'org-id': org_id }) => state[org_id]
);

export const uploadStatus = createSelector(
orgState,
(state) => state.uploadStatus
);

export const uploadDetails = createSelector(
orgState,
(state) => state.uploadDetails
);
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@
</div>
<div class="summary-body last">
<app-authorized [allOf]="['/api/v0/infra/servers/{server_id}', 'put', [server?.id]]">
<chef-button id="sync-button" class="sync-button" secondary primary data-cy="sync-button">Sync Org and Users
<chef-button id="sync-button" class="sync-button" secondary data-cy="sync-org-and-users" (click)="upload.slidePanel()">
<img alt="sync-button" src="/assets/img/sync_arrow.png" class="img-inline"/>
<span>Sync Org and Users</span>
</chef-button>
</app-authorized>
</div>
Expand Down Expand Up @@ -75,6 +77,13 @@
(deleteClicked)="deleteOrg()"
objectAction="Delete">
</app-delete-infra-object-modal>
<app-sync-org-users-slider
#upload
[uploadForm]="uploadZipForm"
[conflictErrorEvent]="conflictErrorEvent"
[isUploaded]="isUploaded"
(uploadClicked)="uploadZipFile($event)">
</app-sync-org-users-slider>
<section class="page-body details-tab" *ngIf="tabValue === 'details'">
<form [formGroup]="updateServerForm">
<chef-form-field>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ chef-page-header {
li {
display: flex;
margin-bottom: 15px;
height: 50px;

span {
font-size: 14px;
Expand Down Expand Up @@ -81,6 +82,12 @@ chef-page-header {
.sync-button {
float: right;
margin-bottom: 0;

.img-inline {
width: 17px;
height: 17px;
margin-right: 5px;
}
}
}
}
Expand Down Expand Up @@ -124,6 +131,7 @@ th {
tbody > .detail-row {
padding-top: 0px;
padding-bottom: 15px;
height: 50px;
}

.detail-row {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, OnInit, OnDestroy, EventEmitter } from '@angular/core';
import { Component, OnInit, OnDestroy, EventEmitter, ViewChild } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MatOptionSelectionChange } from '@angular/material/core/option';
import { Router } from '@angular/router';
Expand Down Expand Up @@ -31,16 +31,19 @@ import {
ValidateWebUIKey
// , GetUsers
} from 'app/entities/servers/server.actions';
import { GetOrgs, CreateOrg, DeleteOrg } from 'app/entities/orgs/org.actions';
import { GetOrgs, CreateOrg, DeleteOrg, UploadZip } from 'app/entities/orgs/org.actions';
import { Org } from 'app/entities/orgs/org.model';
import {
createStatus,
createError,
allOrgs,
getAllStatus as getAllOrgsForServerStatus,
deleteStatus as deleteOrgStatus
deleteStatus as deleteOrgStatus,
uploadStatus,
uploadDetails
} from 'app/entities/orgs/org.selectors';
import { ProjectConstants } from 'app/entities/projects/project.model';
import { SyncOrgUsersSliderComponent } from '../sync-org-users-slider/sync-org-users-slider.component';

export type ChefServerTabName = 'orgs' | 'users' | 'details';
@Component({
Expand Down Expand Up @@ -80,12 +83,17 @@ export class ChefServerDetailsComponent implements OnInit, OnDestroy {
public isServerLoaded = false;
public validating = true;


public updateWebuiKeyForm: FormGroup;
public updatingWebuiKey = false;
public webuiKey: WebUIKey;
public updateWebUIKeySuccessful = false;

public uploadZipForm: FormGroup;
public isUploaded = false;
public migrationID: string;

@ViewChild('upload', { static: false }) upload: SyncOrgUsersSliderComponent;

constructor(
private fb: FormBuilder,
private store: Store<NgrxStateAtom>,
Expand Down Expand Up @@ -144,6 +152,9 @@ export class ChefServerDetailsComponent implements OnInit, OnDestroy {
Validators.pattern(Regex.patterns.VALID_IP_ADDRESS)
]]
});
this.uploadZipForm = this.fb.group({
file: ['', [Validators.required]]
});

this.store.select(routeParams).pipe(
pluck('id'),
Expand Down Expand Up @@ -255,7 +266,23 @@ export class ChefServerDetailsComponent implements OnInit, OnDestroy {
}
});

setTimeout(() => {
combineLatest([
this.store.select(uploadStatus),
this.store.select(uploadDetails)
]).pipe(takeUntil(this.isDestroyed))
.subscribe(([uploadStatusSt, uploadDetailsState]) => {
if (uploadStatusSt === EntityStatus.loadingSuccess && !isNil(uploadDetailsState)) {
// show migration slider
this.isUploaded = true;
this.migrationID = uploadDetailsState.migrationId;
} else if (uploadStatusSt === EntityStatus.loadingFailure) {
// close upload slider with error notification
this.isUploaded = false;
this.upload.closeUploadSlider();
}
});

setTimeout(() => {
if (this.isServerLoaded) {
this.validateWebUIKey(this.server);
}
Expand Down Expand Up @@ -361,4 +388,17 @@ export class ChefServerDetailsComponent implements OnInit, OnDestroy {
private updatingWebuiKeyData(webuikey: WebUIKey) {
this.store.dispatch(new UpdateWebUIKey(webuikey));
}

// upload zip slider functions
public uploadZipFile(file: File): void {
const formData: FormData = new FormData();
if (file) {
formData.append('server_id', this.server.id);
formData.append('file', file);
}
const uploadZipPayload = {
formData: formData
};
this.store.dispatch(new UploadZip( uploadZipPayload ));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { DragDropDirective } from './drag-drop.directive';

describe('DragDropDirective', () => {
it('should create an instance', () => {
const directive = new DragDropDirective();
expect(directive).toBeTruthy();
});
});
Loading

0 comments on commit a0b2375

Please sign in to comment.