Skip to content

Commit 15bbde4

Browse files
dmitriy-borzenkoDmitriy Borzenko
and
Dmitriy Borzenko
authored
Kamu UI 513 modify scheduled updates view (#517)
* modified scheduled updates view * fixed unit tests * changed CHANGELOG.md * removed unnecessary error handling --------- Co-authored-by: Dmitriy Borzenko <[email protected]>
1 parent c5c8b71 commit 15bbde4

20 files changed

+285
-152
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111
### Changed
1212
- Query explainer: improved `INPUT DATA` section view
1313
- added `Run` and `Copy` buttons for a sql code
14+
- Modified "Scheduled updates" view
1415
### Fixed
1516
- Flows table: fixed broken filters in pagination
1617

src/app/common/app.values.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default class AppValues {
1818

1919
public static readonly DISPLAY_DATE_FORMAT = "DD MMM YYYY";
2020
public static readonly DISPLAY_TOOLTIP_DATE_FORMAT = "MMM D, YYYY, HH:mm A";
21-
public static readonly CRON_EXPRESSION_DATE_FORMAT = "MMM Do YYYY, h:mm:ss A";
21+
public static readonly CRON_EXPRESSION_DATE_FORMAT = "MMM Do YYYY, h:mm:ss A ZZ";
2222
public static readonly DISPLAY_FLOW_DATE_FORMAT = "y-MM-dd, h:mm:ss a";
2323
public static readonly TIME_FORMAT = "h:mm:ss A";
2424
public static readonly UNIMPLEMENTED_MESSAGE = "Feature coming soon";

src/app/common/components/flows-table/flows-table.component.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
</a>
8181
<div class="d-flex flex-row truncate-sub-message pe-4">
8282
<div
83-
class="text-small d-block"
83+
class="text-small d-block truncate-sub-message"
8484
[innerHTML]="
8585
descriptionDatasetFlowSubMessage(element, element.description.datasetId) | safeHtml
8686
"

src/app/common/components/flows-table/flows-table.component.scss

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ td.mat-cell {
128128
overflow: hidden;
129129
white-space: nowrap;
130130
text-overflow: ellipsis;
131-
width: 370px;
131+
max-width: 370px;
132132
}
133133

134134
.dynamic-icon {

src/app/common/components/flows-table/flows-table.helpers.mock.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -428,14 +428,14 @@ export const durationBlockTextResults: string[] = [
428428
];
429429

430430
export const tooltipTextResults: string[] = [
431-
"waiting for: Mar 14th 2024, 12:24:29 PM",
432-
"Wake up time: Mar 14th 2024, 2:24:29 PM",
433-
"Wake up time: Mar 14th 2024, 3:24:29 PM",
434-
"Deadline time: Mar 14th 2024, 2:24:29 PM",
435-
"Start running time: Mar 14th 2024, 12:34:29 PM",
436-
"Completed time: Mar 14th 2024, 2:24:29 PM",
437-
"Aborted time: Mar 14th 2024, 11:24:29 AM",
438-
"Start running time: Mar 14th 2024, 11:24:29 AM",
431+
"waiting for: Mar 14th 2024, 12:24:29 PM +0200",
432+
"Wake up time: Mar 14th 2024, 2:24:29 PM +0200",
433+
"Wake up time: Mar 14th 2024, 3:24:29 PM +0200",
434+
"Deadline time: Mar 14th 2024, 2:24:29 PM +0200",
435+
"Start running time: Mar 14th 2024, 12:34:29 PM +0200",
436+
"Completed time: Mar 14th 2024, 2:24:29 PM +0200",
437+
"Aborted time: Mar 14th 2024, 11:24:29 AM +0200",
438+
"Start running time: Mar 14th 2024, 11:24:29 AM +0200",
439439
];
440440

441441
export const mockDatasets: DatasetListFlowsDataFragment[] = [

src/app/dataset-flow/dataset-flow-details/tabs/flow-details-history-tab/flow-details-history-tab.helpers.mock.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -311,13 +311,13 @@ export const flowEventSubMessageResults: string[] = [
311311
"",
312312
"Task #1",
313313
"",
314-
"Wake up time at Feb 12th 2024, 8:22:30 PM, shifted from 8:22:29 PM",
315-
"Activating at Mar 13th 2024, 4:54:30 PM",
314+
"Wake up time at Feb 12th 2024, 8:22:30 PM +0200, shifted from 8:22:29 PM",
315+
"Activating at Mar 13th 2024, 4:54:30 PM +0200",
316316
"Triggered by kamu",
317317
"Input dataset: kamu/alberta.case-details",
318-
"Accumulated 100/500 records. Watermark modified. Deadline at Aug 6th 2022, 12:17:30 AM", //1
318+
"Accumulated 100/500 records. Watermark modified. Deadline at Aug 6th 2022, 12:17:30 AM +0300", //1
319319
"Task #5",
320-
"Wake up time at Mar 13th 2024, 5:54:30 PM",
320+
"Wake up time at Mar 13th 2024, 5:54:30 PM +0200",
321321
"An error occurred, see logs for more details",
322322
"Dataset is up-to-date",
323323
"Ingested 100 new records in 10 new blocks",

src/app/dataset-view/additional-components/dataset-settings-component/tabs/scheduling/batching-trigger-form/batching-trigger-form.component.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@
6363
<button
6464
type="button"
6565
class="btn btn-success"
66-
[disabled]="batchingForm.invalid"
66+
[disabled]="batchingForm.invalid || !batchingUpdatesState.value"
6767
(click)="saveBatchingTriggers()"
6868
data-test-id="save-batching-triggers"
6969
>
70-
Save Trigger
70+
Save
7171
</button>
7272
</div>
7373
</div>

src/app/dataset-view/additional-components/dataset-settings-component/tabs/scheduling/batching-trigger-form/batching-trigger-form.component.spec.ts

+15
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,24 @@ describe("BatchingTriggerFormComponent", () => {
3838

3939
it("should check save batching triggers", () => {
4040
const saveTriggerEmitSpy = spyOn(component.saveTriggerEmit, "emit");
41+
component.batchingForm.patchValue({ updatesState: true });
42+
fixture.detectChanges();
4143

4244
emitClickOnElementByDataTestId(fixture, "save-batching-triggers");
4345

4446
expect(saveTriggerEmitSpy).toHaveBeenCalledTimes(1);
4547
});
48+
49+
it("should check disable all controls", () => {
50+
const disableBatchingEveryTimeSpy = spyOn(component.batchingEveryTime, "disable");
51+
const disableBatchingUnitTimeSpy = spyOn(component.batchingUnitTime, "disable");
52+
const disableBatchingMinRecordsToAwaitSpy = spyOn(component.batchingMinRecordsToAwait, "disable");
53+
54+
component.batchingForm.patchValue({ updatesState: false });
55+
fixture.detectChanges();
56+
57+
expect(disableBatchingEveryTimeSpy).toHaveBeenCalledTimes(1);
58+
expect(disableBatchingUnitTimeSpy).toHaveBeenCalledTimes(1);
59+
expect(disableBatchingMinRecordsToAwaitSpy).toHaveBeenCalledTimes(1);
60+
});
4661
});

src/app/dataset-view/additional-components/dataset-settings-component/tabs/scheduling/batching-trigger-form/batching-trigger-form.component.ts

+27
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ export class BatchingTriggerFormComponent extends BaseComponent implements OnIni
4040

4141
private datasetSchedulingService = inject(DatasetSchedulingService);
4242

43+
public get batchingUpdatesState(): AbstractControl {
44+
return this.batchingForm.controls.updatesState;
45+
}
46+
4347
public get batchingEveryTime(): AbstractControl {
4448
return this.batchingForm.controls.every;
4549
}
@@ -48,6 +52,10 @@ export class BatchingTriggerFormComponent extends BaseComponent implements OnIni
4852
return this.batchingForm.controls.unit;
4953
}
5054

55+
public get batchingMinRecordsToAwait(): AbstractControl {
56+
return this.batchingForm.controls.minRecordsToAwait;
57+
}
58+
5159
public saveBatchingTriggers(): void {
5260
this.saveTriggerEmit.emit(this.batchingForm);
5361
}
@@ -63,6 +71,11 @@ export class BatchingTriggerFormComponent extends BaseComponent implements OnIni
6371
public ngOnInit(): void {
6472
this.setBatchingEveryTimeValidator();
6573
this.initBatchingForm();
74+
this.batchingUpdatesState.valueChanges
75+
.pipe(takeUntilDestroyed(this.destroyRef))
76+
.subscribe((enableUpdates: boolean) => {
77+
enableUpdates ? this.enableControls() : this.disableControls();
78+
});
6679
}
6780

6881
public initBatchingForm(): void {
@@ -78,7 +91,21 @@ export class BatchingTriggerFormComponent extends BaseComponent implements OnIni
7891
minRecordsToAwait: batching.minRecordsToAwait,
7992
updatesState: !flowTriggers.paused,
8093
});
94+
} else {
95+
this.disableControls();
8196
}
8297
});
8398
}
99+
100+
private disableControls(): void {
101+
this.batchingEveryTime.disable();
102+
this.batchingUnitTime.disable();
103+
this.batchingMinRecordsToAwait.disable();
104+
}
105+
106+
private enableControls(): void {
107+
this.batchingEveryTime.enable();
108+
this.batchingUnitTime.enable();
109+
this.batchingMinRecordsToAwait.enable();
110+
}
84111
}

src/app/dataset-view/additional-components/dataset-settings-component/tabs/scheduling/dataset-settings-scheduling-tab.component.html

+17-4
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,35 @@
22
<h2>Scheduled updates</h2>
33
<mat-divider class="mt-1 mb-2" />
44
<div class="box mt-3 p-4" *ngIf="isRootDataset">
5-
<div class="d-flex flex-row gap-4">
5+
<div class="d-flex flex-column gap-4">
66
<div class="w-100">
77
<app-ingest-trigger-form
88
[datasetBasics]="datasetBasics"
99
[updateStateToggleLabel]="'Enable/Disable updates'"
10-
(saveTriggerEmit)="savePollingTriggers($event)"
10+
(changeTriggerEmit)="changePollingTriggers($event)"
1111
/>
1212
</div>
1313

14-
<div class="w-75">
14+
<div class="w-100">
1515
<app-ingest-configuration-form
16+
*ngIf="pollingForm"
1617
[datasetBasics]="datasetBasics"
17-
(saveConfigurationEmit)="saveIngestConfiguration($event)"
18+
[disabled]="pollingForm.controls.updatesState.value"
19+
(changeConfigurationEmit)="changeIngestConfiguration($event)"
1820
/>
1921
</div>
2022
</div>
23+
<div class="mt-4 d-flex justify-content-end">
24+
<button
25+
type="button"
26+
class="btn btn-success"
27+
(click)="saveScheduledUpdates()"
28+
[disabled]="invalidPollingForm || !enablePollingUpdates"
29+
data-test-id="save-polling-triggers"
30+
>
31+
Save
32+
</button>
33+
</div>
2134
</div>
2235

2336
<div *ngIf="!isRootDataset">

src/app/dataset-view/additional-components/dataset-settings-component/tabs/scheduling/dataset-settings-scheduling-tab.component.spec.ts

+23-26
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { DatasetSettingsSchedulingTabComponent } from "./dataset-settings-schedu
33
import { Apollo } from "apollo-angular";
44
import { ApolloTestingModule } from "apollo-angular/testing";
55
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
6-
import { ToastrModule, ToastrService } from "ngx-toastr";
6+
import { ToastrModule } from "ngx-toastr";
77
import { SharedTestModule } from "src/app/common/shared-test.module";
88
import { MatDividerModule } from "@angular/material/divider";
99
import { MatSlideToggleModule } from "@angular/material/slide-toggle";
@@ -40,7 +40,6 @@ describe("DatasetSettingsSchedulingTabComponent", () => {
4040
let component: DatasetSettingsSchedulingTabComponent;
4141
let fixture: ComponentFixture<DatasetSettingsSchedulingTabComponent>;
4242
let datasetSchedulingService: DatasetSchedulingService;
43-
let toastrService: ToastrService;
4443

4544
const MOCK_PARAM_EVERY = 10;
4645
const MOCK_PARAM_UNIT = TimeUnit.Minutes;
@@ -72,7 +71,6 @@ describe("DatasetSettingsSchedulingTabComponent", () => {
7271

7372
fixture = TestBed.createComponent(DatasetSettingsSchedulingTabComponent);
7473
datasetSchedulingService = TestBed.inject(DatasetSchedulingService);
75-
toastrService = TestBed.inject(ToastrService);
7674

7775
component = fixture.componentInstance;
7876
component.datasetBasics = mockDatasetBasicsRootFragment;
@@ -109,23 +107,6 @@ describe("DatasetSettingsSchedulingTabComponent", () => {
109107
expect(fetchUncacheableCheckBox.value).toEqual(TimeUnit.Hours);
110108
});
111109

112-
it("should check save ingest configuration", () => {
113-
const mockConfigurationForm = new FormGroup<IngestConfigurationFormType>({
114-
fetchUncacheable: new FormControl<boolean>(false, { nonNullable: true }),
115-
});
116-
component.datasetPermissions = _.cloneDeep(mockFullPowerDatasetPermissionsFragment);
117-
fixture.detectChanges();
118-
const setDatasetFlowScheduleSpy = spyOn(datasetSchedulingService, "setDatasetFlowConfigs").and.returnValue(
119-
of(true),
120-
);
121-
const toastrServiceSpy = spyOn(toastrService, "success");
122-
123-
component.saveIngestConfiguration(mockConfigurationForm);
124-
125-
expect(setDatasetFlowScheduleSpy).toHaveBeenCalledTimes(1);
126-
expect(toastrServiceSpy).toHaveBeenCalledTimes(1);
127-
});
128-
129110
it("should check 'Save triger' button works for DERIVATIVE dataset", () => {
130111
const setDatasetFlowBatchingSpy = spyOn(datasetSchedulingService, "setDatasetTriggers").and.callThrough();
131112
component.datasetPermissions = _.cloneDeep(mockFullPowerDatasetPermissionsFragment);
@@ -162,12 +143,15 @@ describe("DatasetSettingsSchedulingTabComponent", () => {
162143
);
163144
});
164145

165-
it("should check 'Save trigger' button works for ROOT dataset with time delta", () => {
146+
it("should check 'Save' button works for ROOT dataset with time delta", () => {
166147
const setDatasetFlowScheduleSpy = spyOn(datasetSchedulingService, "setDatasetTriggers").and.callThrough();
148+
const setDatasetFlowConfigsSpy = spyOn(datasetSchedulingService, "setDatasetFlowConfigs").and.returnValue(
149+
of(true),
150+
);
167151
component.datasetPermissions = _.cloneDeep(mockFullPowerDatasetPermissionsFragment);
168152

169153
const mockPollingTriggerForm = new FormGroup<PollingGroupType>({
170-
updatesState: new FormControl<boolean>(false, { nonNullable: true }),
154+
updatesState: new FormControl<boolean>(true, { nonNullable: true }),
171155
__typename: new FormControl(PollingGroupEnum.TIME_DELTA, [Validators.required]),
172156
every: new FormControl<MaybeNull<number>>({ value: MOCK_PARAM_EVERY, disabled: false }, [
173157
Validators.required,
@@ -181,9 +165,14 @@ describe("DatasetSettingsSchedulingTabComponent", () => {
181165
cronExpressionValidator(),
182166
]),
183167
});
168+
component.pollingForm = mockPollingTriggerForm;
169+
component.ingestConfigurationForm = new FormGroup<IngestConfigurationFormType>({
170+
fetchUncacheable: new FormControl<boolean>(false, { nonNullable: true }),
171+
});
184172

185-
component.savePollingTriggers(mockPollingTriggerForm);
173+
component.saveScheduledUpdates();
186174

175+
expect(setDatasetFlowConfigsSpy).toHaveBeenCalledTimes(1);
187176
expect(setDatasetFlowScheduleSpy).toHaveBeenCalledWith(
188177
jasmine.objectContaining({
189178
triggerInput: {
@@ -195,12 +184,15 @@ describe("DatasetSettingsSchedulingTabComponent", () => {
195184
);
196185
});
197186

198-
it("should check 'Save trigger' button works for ROOT dataset with cron expression", () => {
187+
it("should check 'Save' button works for ROOT dataset with cron expression", () => {
199188
const setDatasetFlowScheduleSpy = spyOn(datasetSchedulingService, "setDatasetTriggers").and.callThrough();
189+
const setDatasetFlowConfigsSpy = spyOn(datasetSchedulingService, "setDatasetFlowConfigs").and.returnValue(
190+
of(true),
191+
);
200192
component.datasetPermissions = _.cloneDeep(mockFullPowerDatasetPermissionsFragment);
201193

202194
const mockPollingTriggerForm = new FormGroup<PollingGroupType>({
203-
updatesState: new FormControl<boolean>(false, { nonNullable: true }),
195+
updatesState: new FormControl<boolean>(true, { nonNullable: true }),
204196
__typename: new FormControl(PollingGroupEnum.CRON_5_COMPONENT_EXPRESSION, [Validators.required]),
205197
every: new FormControl<MaybeNull<number>>({ value: null, disabled: false }, [
206198
Validators.required,
@@ -212,9 +204,14 @@ describe("DatasetSettingsSchedulingTabComponent", () => {
212204
cronExpressionValidator(),
213205
]),
214206
});
207+
component.pollingForm = mockPollingTriggerForm;
208+
component.ingestConfigurationForm = new FormGroup<IngestConfigurationFormType>({
209+
fetchUncacheable: new FormControl<boolean>(false, { nonNullable: true }),
210+
});
215211

216-
component.savePollingTriggers(mockPollingTriggerForm);
212+
component.saveScheduledUpdates();
217213

214+
expect(setDatasetFlowConfigsSpy).toHaveBeenCalledTimes(1);
218215
expect(setDatasetFlowScheduleSpy).toHaveBeenCalledWith(
219216
jasmine.objectContaining({
220217
triggerInput: {

0 commit comments

Comments
 (0)