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

Stalwart 32 upload slider functionality #6654

Merged
merged 11 commits into from
Feb 1, 2022
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
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