Skip to content

Commit

Permalink
(feat/fix): Enable Partial Sink Update (#2230)
Browse files Browse the repository at this point in the history
* wip: sink view init

* wip: sink view + edit

* wip: config editor

* wip: save

* fix edit config & save

* update sink config view css

* fix json format
  • Loading branch information
gpazuch authored May 9, 2023
1 parent ebd5e55 commit 52234c7
Show file tree
Hide file tree
Showing 18 changed files with 647 additions and 1 deletion.
6 changes: 6 additions & 0 deletions ui/src/app/pages/pages-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { DashboardComponent } from 'app/pages/dashboard/dashboard.component';
import { DatasetAddComponent } from 'app/pages/datasets/add/dataset.add.component';
import { ProfileComponent } from './profile/profile.component';
import { AgentPolicyViewComponent } from 'app/pages/datasets/policies.agent/view/agent.policy.view.component';
import { SinkViewComponent } from './sinks/view/sink.view.component';

const children = [
{
Expand Down Expand Up @@ -116,6 +117,11 @@ const children = [
component: SinkAddComponent,
data: {breadcrumb: 'Edit Sink'},
},
{
path: 'view/:id',
component: SinkViewComponent,
data: {breadcrumb: 'View Sink'},
},
],
},
{
Expand Down
2 changes: 2 additions & 0 deletions ui/src/app/pages/pages.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { DashboardModule } from './dashboard/dashboard.module';
import { AgentViewComponent } from './fleet/agents/view/agent.view.component';
import { PagesRoutingModule } from './pages-routing.module';
import { PagesComponent } from './pages.component';
import { SinkViewComponent } from './sinks/view/sink.view.component';

@NgModule({
imports: [
Expand Down Expand Up @@ -150,6 +151,7 @@ import { PagesComponent } from './pages.component';
SinkAddComponent,
SinkDetailsComponent,
SinkDeleteComponent,
SinkViewComponent,
// DEV SHOWCASE
ShowcaseComponent,
],
Expand Down
2 changes: 1 addition & 1 deletion ui/src/app/pages/sinks/list/sink.list.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ <h4>{{ strings.list.header }}</h4>
>
<div class="block">
<button
(click)="openDetailsModal(row)"
(click)="onOpenView(row)"
class="orb-action-hover detail-button"
ghost
nbTooltip="View Details"
Expand Down
7 changes: 7 additions & 0 deletions ui/src/app/pages/sinks/list/sink.list.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,13 @@ export class SinkListComponent implements AfterViewInit, AfterViewChecked, OnDes
});
}

onOpenView(sink: any) {
this.router.navigate([`view/${sink.id}`], {
relativeTo: this.route,
state: { sink: sink },
});
}

openDeleteModal(row: any) {
const { id } = row;
this.dialogService
Expand Down
42 changes: 42 additions & 0 deletions ui/src/app/pages/sinks/view/sink.view.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<header data-orb-qa-id="sink#view" class="row">
<div class="col-7">
<xng-breadcrumb class="orb-breadcrumb" data-orb-qa-id="breadcrumb">
<ng-container
*xngBreadcrumbItem="let breadcrumb; let info = info; let first = first"
>
<ng-container [ngClass]="{ my_class: first === '' }">{{
breadcrumb
}}</ng-container>
</ng-container>
</xng-breadcrumb>
<h4>{{ strings.sink.view.header }}</h4>
</div>
<div class="col-3">
<button
(click)="save()"
[disabled]="!canSave()"
class="policy-save"
nbButton
*ngIf="isEditMode()"
shape="round"
>
Save
</button>
<button
(click)="discard()"
class="policy-discard"
nbButton
shape="round"
*ngIf="isEditMode()"
>
Discard
</button>
</div>
</header>

<div *ngIf="!isLoading" class="row">
<ngx-sink-details [(editMode)]="editMode.details" [sink]="sink">
</ngx-sink-details>
<ngx-sink-config [(editMode)]="editMode.config" [sink]="sink">
</ngx-sink-config>
</div>
72 changes: 72 additions & 0 deletions ui/src/app/pages/sinks/view/sink.view.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
button {
margin: 0 3px;

&.policy-duplicate {
float: right;
color: #fff !important;
font-family: "Montserrat", sans-serif;
font-weight: 700;
text-transform: none !important;

&.btn-disabled {
background: #2b3148;
}

&:not(.btn-disabled) {
background-color: #3089fc !important;
}
}

&.policy-save {
float: right;
color: #fff !important;
font-family: "Montserrat", sans-serif;
font-weight: 500;
text-transform: none !important;

&.btn-disabled {
background: #2b3148;
}

&:not(.btn-disabled) {
background-color: #3089fc !important;
}
}

&.policy-discard {
float: right;
color: #fff !important;
font-family: "Montserrat", sans-serif;
font-weight: 500;
text-transform: none !important;

&.btn-disabled {
background: #2b3148;
}

&:not(.btn-disabled) {
background-color: #df316f !important;
}
}
}

ngx-sink-details {
flex: 0 1 22rem;
}

ngx-sink-config {
flex: 2 1 auto;
min-height: 30rem !important;

nb-card {
height: 30rem !important;
}
}

.row {
gap: 1rem;
}

header {
justify-content: space-between;
}
25 changes: 25 additions & 0 deletions ui/src/app/pages/sinks/view/sink.view.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { SinkViewComponent } from './sink.view.component';

describe('SinkViewComponent', () => {
let component: SinkViewComponent;
let fixture: ComponentFixture<SinkViewComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SinkViewComponent ]
})
.compileComponents();
}));

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

it('should create', () => {
expect(component).toBeTruthy();
});
});
128 changes: 128 additions & 0 deletions ui/src/app/pages/sinks/view/sink.view.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { ChangeDetectorRef, Component, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Sink } from 'app/common/interfaces/orb/sink.interface';
import { NotificationsService } from 'app/common/services/notifications/notifications.service';
import { SinksService } from 'app/common/services/sinks/sinks.service';
import { SinkConfigComponent } from 'app/shared/components/orb/sink/sink-config/sink-config.component';
import { SinkDetailsComponent } from 'app/shared/components/orb/sink/sink-details/sink-details.component';
import { STRINGS } from 'assets/text/strings';
import { Subscription } from 'rxjs';

@Component({
selector: 'ngx-sink-view',
templateUrl: './sink.view.component.html',
styleUrls: ['./sink.view.component.scss']
})
export class SinkViewComponent implements OnInit, OnChanges, OnDestroy {
strings = STRINGS;

isLoading = false;

sink: Sink;

sinkId = '';

sinkSubscription: Subscription;

editMode = {
details: false,
config: false,
}

@ViewChild(SinkDetailsComponent) detailsComponent: SinkDetailsComponent;

@ViewChild(SinkConfigComponent)
configComponent: SinkConfigComponent;

constructor(private cdr: ChangeDetectorRef,
private notifications: NotificationsService,
private sinks: SinksService,
private route: ActivatedRoute,
) { }

ngOnInit(): void {
this.fetchData();
}

ngOnChanges(): void {
this.fetchData();
}

fetchData() {
this.isLoading = true;
this.sinkId = this.route.snapshot.paramMap.get('id');
this.retrieveSink();
}

isEditMode() {
return Object.values(this.editMode).reduce(
(prev, cur) => prev || cur,
false,
);
}

canSave() {
const detailsValid = this.editMode.details
? this.detailsComponent?.formGroup?.status === 'VALID'
: true;

const configValid = this.editMode.config
? this.configComponent?.formControl?.status === 'VALID'
: true;

return detailsValid && configValid;
}

discard() {
this.editMode.details = false;
this.editMode.config = false;
}

save() {
const { name, description, id, backend } = this.sink;

const sinkDetails = this.detailsComponent.formGroup?.value;
const tags = this.detailsComponent.selectedTags;
const config = this.configComponent.code;

const detailsPartial = (!!this.editMode.details && { ...sinkDetails, id, backend})
|| { name, description, id, backend };

let configPartial = (!!this.editMode.config && JSON.parse(config)) || {};

const payload = {
...configPartial,
...detailsPartial,
tags,

} as Sink;

try {
this.sinks.editSink(payload).subscribe((resp) => {
this.discard();
this.sink = resp;
this.fetchData();
this.notifications.success('Sink updated successfully', '');
});
} catch (err) {
this.notifications.error(
'Failed to edit Sink',
'Error: Invalid configuration',
)
}
}

retrieveSink() {
this.sinkSubscription = this.sinks
.getSinkById(this.sinkId)
.subscribe(sink => {
this.sink = sink;
this.isLoading = false;
this.cdr.markForCheck();
});
}

ngOnDestroy(): void {
this.sinkSubscription.unsubscribe();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<nb-card>
<nb-card-header class="summary-accent">Sink Backend Configuration
<button
(click)="toggleEdit(true)"
*ngIf="!editMode"
class="card-button"
nbButton
status="primary">
Edit
</button>
<button
(click)="toggleEdit(false)"
*ngIf="editMode"
class="card-button"
nbButton
status="danger">
Discard
</button>
</nb-card-header>
<nb-card-body>
<!-- <div *ngIf="editMode; then updateView else readView"></div>-->
<div class="code-editor-wrapper">
<ngx-monaco-editor
#editorComponent
[(ngModel)]="code"
[options]="editorOptions"
class="code-editor"
ngDefaultControl>
</ngx-monaco-editor>
</div>
</nb-card-body>
</nb-card>

<ng-template #readView>
<div class="d-flex flex-column">
<pre
innerHtml="{{ (sink?.config | prettyJson: [false, 2]) }}"></pre>
</div>
</ng-template>

<ng-template #updateView>
<pre
innerHtml="{{ (sink?.config | prettyJson: [false, 2]) }}"></pre>
</ng-template>

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ngx-monaco-editor {
height: 25rem;
}
Loading

0 comments on commit 52234c7

Please sign in to comment.