Skip to content

Commit

Permalink
feat: add support to configure metadata gcs bucket (#835)
Browse files Browse the repository at this point in the history
* feat: add support to configure metadata gcs bucket

* test

* comment changes

* comment changes
  • Loading branch information
asthamohta committed Jun 14, 2024
1 parent 934e2b1 commit a094afc
Show file tree
Hide file tree
Showing 14 changed files with 296 additions and 17 deletions.
2 changes: 1 addition & 1 deletion ui/dist/ui/index.html

Large diffs are not rendered by default.

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions ui/src/app/app.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ export enum MigrationDetails {
GeneratingResources = 'generatingResources',
NumberOfShards = 'numberOfShards',
NumberOfInstances = 'numberOfInstances',
isForeignKeySkipped = 'isForeignKeySkipped'
isForeignKeySkipped = 'isForeignKeySkipped',
IsGcsMetadataPathSet = 'isGcsMetadataPathSet'
}

export enum TargetDetails {
Expand All @@ -82,7 +83,9 @@ export enum TargetDetails {
SourceConnProfile = 'sourceConnProfile',
TargetConnProfile = 'targetConnProfile',
ReplicationSlot = 'replicationSlot',
Publication = 'publication'
Publication = 'publication',
GcsMetadataName = 'gcsName',
GcsMetadataRootPath = 'gcsRootPath'
}

export const Profile = {
Expand Down Expand Up @@ -168,4 +171,10 @@ export const dialogConfigDropComponent: MatDialogConfig<any> = {
width: '100%',
minWidth: '50%',
maxWidth: '75%',
}

export const dialogDefault: MatDialogConfig<any> = {
width: '30vw',
minWidth: '400px',
maxWidth: '500px',
}
2 changes: 2 additions & 0 deletions ui/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { DropObjectDetailDialogComponent } from './components/drop-object-detail
import { DatabaseLoaderComponent } from './components/database-loader/database-loader.component'
import { PrepareMigrationComponent } from './components/prepare-migration/prepare-migration.component'
import { TargetDetailsFormComponent } from './components/target-details-form/target-details-form.component'
import { GcsMetadataDetailsFormComponent } from './components/gcs-metadata-details-form/gcs-metadata-details-form.component'
import { ConnectionProfileFormComponent } from './components/connection-profile-form/connection-profile-form.component'
import { SourceDetailsFormComponent } from './components/source-details-form/source-details-form.component'
import { SidenavReviewChangesComponent } from './components/sidenav-review-changes/sidenav-review-changes.component'
Expand Down Expand Up @@ -84,6 +85,7 @@ import { AddNewSequenceComponent } from './components/add-new-sequence/add-new-s
DropObjectDetailDialogComponent,
PrepareMigrationComponent,
TargetDetailsFormComponent,
GcsMetadataDetailsFormComponent,
ConnectionProfileFormComponent,
SidenavReviewChangesComponent,
TableColumnChangesPreviewComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<div mat-dialog-content>
<form [formGroup]="gcsMetadataDetailsForm" class="gcs-metadata-detail-form">
<h2>GCS Metadata Storage<mat-icon class="configure"
matTooltip='Specify existing or new GCS path to store metadata for migration'>
info</mat-icon></h2>

<mat-form-field class="full-width" appearance="outline">
<mat-label>GCS Bucket Name</mat-label>
<input matInput placeholder="GCS Bucket Name" type="text" formControlName="gcsName"
ng-value="TargetDetails.GcsMetadataName" />
</mat-form-field>
<br>
<mat-form-field class="full-width" appearance="outline">
<mat-label>Path Prefix</mat-label>
<input matInput placeholder="Path Prefix" type="text" formControlName="gcsRootPath"
ng-value="TargetDetails.GcsMetadataRootPath" />
</mat-form-field>
<br>
</form>
<div mat-dialog-actions class="buttons-container">
<button mat-button color="primary" mat-dialog-close>Cancel</button>
<button mat-button type="submit" color="primary" [disabled]="!(gcsMetadataDetailsForm.valid)"
(click)="updateGcsPathMetadataDetails()">
Save
</button>
</div>
</div>
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { GcsMetadataDetailsFormComponent } from './gcs-metadata-details-form.component';
import { TargetDetails, MigrationDetails } from 'src/app/app.constants';

describe('GcsMetadataDetailsFormComponent', () => {
let component: GcsMetadataDetailsFormComponent;
let fixture: ComponentFixture<GcsMetadataDetailsFormComponent>;
let dialogRefSpy: jasmine.SpyObj<MatDialogRef<GcsMetadataDetailsFormComponent>>;

beforeEach(async () => {
dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['close']);

await TestBed.configureTestingModule({
declarations: [GcsMetadataDetailsFormComponent],
imports: [ReactiveFormsModule, MatSnackBarModule],
providers: [
{
provide: MatDialogRef,
useValue: dialogRefSpy,
},
{
provide: MAT_DIALOG_DATA,
useValue: {},
},
],
}).compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(GcsMetadataDetailsFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should update localStorage and close the dialog', () => {
spyOn(localStorage, 'setItem');
component.gcsMetadataDetailsForm.setValue({
gcsName: 'testGcsName',
gcsRootPath: 'testGcsRootPath'
});

component.updateGcsPathMetadataDetails();

expect(localStorage.setItem).toHaveBeenCalledWith(TargetDetails.GcsMetadataName, 'testGcsName');
expect(localStorage.setItem).toHaveBeenCalledWith(TargetDetails.GcsMetadataRootPath, 'testGcsRootPath');
expect(localStorage.setItem).toHaveBeenCalledWith(MigrationDetails.IsGcsMetadataPathSet, 'true');
expect(dialogRefSpy.close).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Component, OnInit } from '@angular/core'
import { FormBuilder, FormGroup, Validators } from '@angular/forms'
import { MatDialogRef} from '@angular/material/dialog'
import { MigrationDetails, TargetDetails } from 'src/app/app.constants'

@Component({
selector: 'app-gcs-metadata-details-form',
templateUrl: './gcs-metadata-details-form.component.html',
styleUrls: ['./gcs-metadata-details-form.component.scss'],
})
export class GcsMetadataDetailsFormComponent implements OnInit {
gcsMetadataDetailsForm: FormGroup

constructor(
private formBuilder: FormBuilder,
private dialogRef: MatDialogRef<GcsMetadataDetailsFormComponent>,
) {
this.gcsMetadataDetailsForm = this.formBuilder.group({
gcsName: ['', [Validators.required, Validators.pattern('^(?!\.$)(?!\.\.$)(?!.*[\r\n])[^\r\n]{1,1024}$')]],
gcsRootPath: [''],
})

this.gcsMetadataDetailsForm.setValue({
gcsName: localStorage.getItem(TargetDetails.GcsMetadataName) || '',
gcsRootPath: localStorage.getItem(TargetDetails.GcsMetadataRootPath) || ''
})
}

ngOnInit(): void {}

updateGcsPathMetadataDetails() {
let formValue = this.gcsMetadataDetailsForm.value
localStorage.setItem(TargetDetails.GcsMetadataName, formValue.gcsName || '')
localStorage.setItem(TargetDetails.GcsMetadataRootPath, formValue.gcsRootPath || '')
localStorage.setItem(MigrationDetails.IsGcsMetadataPathSet, "true")
this.dialogRef.close()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,24 @@ <h3>Source database details:</h3>
</mat-icon>
</button></span>
</p>
<p class="point" *ngIf="isSharded">
<span class="bullet" *ngIf="(selectedMigrationMode ==='Schema')">2</span>
<span class="bullet" *ngIf="!(selectedMigrationMode ==='Schema')">6</span>
<span>GCS Metadata Path (Optional)
<mat-icon class="configure"
matTooltip="Specify existing or new GCS path to store metadata for migration.">
info</mat-icon>
</span>
<span><button mat-button (click)="openGcsMetadataDetailsForm()"
[disabled]="isMigrationInProgress || !isTargetDetailSet" color="primary">
Configure
<mat-icon iconPositionEnd>edit</mat-icon>
<mat-icon iconPositionEnd *ngIf="isGcsMetadataDetailSet" class="success"
matTooltip="GCS Metadata Path Configured" matTooltipPosition="above">
check_circle
</mat-icon>
</button></span>
</p>
<p class="point"
*ngIf="(selectedMigrationType ==='lowdt') && !(selectedMigrationMode ==='Schema') && isSharded">
<span class="bullet" *ngIf="isSharded">5</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,36 @@
import { HttpClientModule } from '@angular/common/http';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatDialogModule } from '@angular/material/dialog';
import { MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { of } from 'rxjs';

import { PrepareMigrationComponent } from './prepare-migration.component';
import { TargetDetails, MigrationDetails } from 'src/app/app.constants';
import { GcsMetadataDetailsFormComponent } from '../gcs-metadata-details-form/gcs-metadata-details-form.component';

describe('PrepareMigrationComponent', () => {
let component: PrepareMigrationComponent;
let fixture: ComponentFixture<PrepareMigrationComponent>;
let dialog: MatDialog;
let dialogRefSpy: jasmine.SpyObj<MatDialogRef<GcsMetadataDetailsFormComponent>>;

beforeEach(async () => {
dialogRefSpy = jasmine.createSpyObj('MatDialogRef', ['afterClosed']);

await TestBed.configureTestingModule({
declarations: [ PrepareMigrationComponent ],
imports: [MatDialogModule, HttpClientModule, MatSnackBarModule]
})
.compileComponents();
declarations: [PrepareMigrationComponent],
imports: [MatDialogModule, HttpClientModule, MatSnackBarModule],
providers: [
{ provide: MatDialogRef, useValue: dialogRefSpy },
{ provide: MatDialog, useValue: { open: () => dialogRefSpy } }
]
}).compileComponents();

fixture = TestBed.createComponent(PrepareMigrationComponent);
component = fixture.componentInstance;
dialog = TestBed.inject(MatDialog);

dialogRefSpy.afterClosed.and.returnValue(of(true));
});

beforeEach(() => {
Expand All @@ -26,4 +42,43 @@ describe('PrepareMigrationComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});

it('should open the dialog and set values after closed', () => {
const mockValues: { [key: string]: string } = {
[TargetDetails.TargetDB]: 'mockTargetDB',
[TargetDetails.SourceConnProfile]: 'mockSourceConnProfile',
[TargetDetails.TargetConnProfile]: 'mockTargetConnProfile',
[TargetDetails.ReplicationSlot]: 'mockReplicationSlot',
[TargetDetails.Publication]: 'mockPublication',
[TargetDetails.GcsMetadataName]: 'mockGcsMetadataName',
[TargetDetails.GcsMetadataRootPath]: 'mockGcsMetadataRootPath',
[MigrationDetails.IsGcsMetadataPathSet]: 'true'
};

spyOn(localStorage, 'getItem').and.callFake((key: string) => mockValues[key]);
const dialogOpenSpy = spyOn(dialog, 'open').and.returnValue(dialogRefSpy);

component.openGcsMetadataDetailsForm();

expect(dialogOpenSpy).toHaveBeenCalledWith(GcsMetadataDetailsFormComponent, {
width: '30vw',
minWidth: '400px',
maxWidth: '500px',
});

dialogRefSpy.afterClosed().subscribe(() => {
expect(component.isGcsMetadataDetailSet).toBeTrue();
expect(component.targetDetails).toEqual({
TargetDB: 'mockTargetDB',
SourceConnProfile: 'mockSourceConnProfile',
TargetConnProfile: 'mockTargetConnProfile',
ReplicationSlot: 'mockReplicationSlot',
Publication: 'mockPublication',
GcsMetadataPath: {
GcsBucketName: 'mockGcsMetadataName',
GcsBucketRootPath: 'mockGcsMetadataRootPath',
},
});
});
});
});
Loading

0 comments on commit a094afc

Please sign in to comment.