From ede25f1b457467915cbde44ab7a283fb2fa38d40 Mon Sep 17 00:00:00 2001 From: Chi Cao Minh Date: Thu, 1 Oct 2020 23:59:21 -0700 Subject: [PATCH] Fix UI datasources view edit action compaction (#10459) Restore the web console's ability to view a datasource's compaction configuration via the "action" menu. Refactoring done in https://github.com/apache/druid/pull/10438 introduced a regression that always caused the default compaction configuration to be shown via the "action" menu instead. Regression test is added in e2e-tests/auto-compaction.spec.ts. --- web-console/e2e-tests/auto-compaction.spec.ts | 5 ++ .../component/datasources/overview.ts | 22 +++++- .../component/load-data/config/partition.ts | 77 +++++++++++++++++-- web-console/e2e-tests/util/playwright.ts | 7 ++ .../views/datasource-view/datasource-view.tsx | 4 +- 5 files changed, 104 insertions(+), 11 deletions(-) diff --git a/web-console/e2e-tests/auto-compaction.spec.ts b/web-console/e2e-tests/auto-compaction.spec.ts index 512c2724b8c..68cd00b0287 100644 --- a/web-console/e2e-tests/auto-compaction.spec.ts +++ b/web-console/e2e-tests/auto-compaction.spec.ts @@ -126,6 +126,11 @@ async function configureCompaction( ) { const datasourcesOverview = new DatasourcesOverview(page, UNIFIED_CONSOLE_URL); await datasourcesOverview.setCompactionConfiguration(datasourceName, compactionConfig); + + const savedCompactionConfig = await datasourcesOverview.getCompactionConfiguration( + datasourceName, + ); + expect(savedCompactionConfig).toEqual(compactionConfig); } async function triggerCompaction() { diff --git a/web-console/e2e-tests/component/datasources/overview.ts b/web-console/e2e-tests/component/datasources/overview.ts index 54af58b7f0c..11f44b48d41 100644 --- a/web-console/e2e-tests/component/datasources/overview.ts +++ b/web-console/e2e-tests/component/datasources/overview.ts @@ -19,8 +19,10 @@ import * as playwright from 'playwright-core'; import { clickButton } from '../../util/playwright'; +import { getLabeledInput } from '../../util/playwright'; import { setLabeledInput } from '../../util/playwright'; import { extractTable } from '../../util/table'; +import { readPartitionSpec } from '../load-data/config/partition'; import { CompactionConfig } from './compaction'; import { Datasource } from './datasource'; @@ -42,6 +44,9 @@ enum DatasourceColumn { ACTIONS, } +const EDIT_COMPACTION_CONFIGURATION = 'Edit compaction configuration'; +const SKIP_OFFSET_FROM_LATEST = 'Skip offset from latest'; + /** * Represents datasource overview tab. */ @@ -80,10 +85,10 @@ export class DatasourcesOverview { ): Promise { await this.openEditActions(datasourceName); - await this.page.click('"Edit compaction configuration"'); + await this.page.click(`"${EDIT_COMPACTION_CONFIGURATION}"`); await setLabeledInput( this.page, - 'Skip offset from latest', + SKIP_OFFSET_FROM_LATEST, compactionConfig.skipOffsetFromLatest, ); await compactionConfig.partitionsSpec.apply(this.page); @@ -91,6 +96,17 @@ export class DatasourcesOverview { await clickButton(this.page, 'Submit'); } + async getCompactionConfiguration(datasourceName: string): Promise { + await this.openEditActions(datasourceName); + + await this.page.click(`"${EDIT_COMPACTION_CONFIGURATION}"`); + const skipOffsetFromLatest = await getLabeledInput(this.page, SKIP_OFFSET_FROM_LATEST); + const partitionsSpec = await readPartitionSpec(this.page); + + await clickButton(this.page, 'Close'); + return new CompactionConfig({ skipOffsetFromLatest, partitionsSpec: partitionsSpec! }); + } + private async openEditActions(datasourceName: string): Promise { const datasources = await this.getDatasources(); const index = datasources.findIndex(t => t.name === datasourceName); @@ -100,6 +116,6 @@ export class DatasourcesOverview { const editActions = await this.page.$$('span[icon=wrench]'); editActions[index].click(); - await this.page.waitFor(5000); + await this.page.waitFor('ul.bp3-menu'); } } diff --git a/web-console/e2e-tests/component/load-data/config/partition.ts b/web-console/e2e-tests/component/load-data/config/partition.ts index 8431eccab98..51713c6efda 100644 --- a/web-console/e2e-tests/component/load-data/config/partition.ts +++ b/web-console/e2e-tests/component/load-data/config/partition.ts @@ -19,6 +19,7 @@ import * as playwright from 'playwright-core'; import { selectSuggestibleInput } from '../../../util/playwright'; +import { getLabeledInput } from '../../../util/playwright'; import { setLabeledInput } from '../../../util/playwright'; /* tslint:disable max-classes-per-file */ @@ -40,22 +41,49 @@ export interface PartitionsSpec { apply(page: playwright.Page): Promise; } +export async function readPartitionSpec(page: playwright.Page): Promise { + const type = await getLabeledInput(page, PARTITIONING_TYPE); + switch (type) { + case HashedPartitionsSpec.TYPE: + return HashedPartitionsSpec.read(page); + case SingleDimPartitionsSpec.TYPE: + return SingleDimPartitionsSpec.read(page); + } + return null; +} + export class HashedPartitionsSpec implements PartitionsSpec { + public static TYPE = 'hashed'; + private static NUM_SHARDS = 'Num shards'; + readonly type: string; + static async read(page: playwright.Page): Promise { + const numShards = await getLabeledInputAsNumber(page, HashedPartitionsSpec.NUM_SHARDS); + return new HashedPartitionsSpec({ numShards }); + } + constructor(props: HashedPartitionsSpecProps) { Object.assign(this, props); - this.type = 'hashed'; + this.type = HashedPartitionsSpec.TYPE; } async apply(page: playwright.Page): Promise { await setLabeledInput(page, PARTITIONING_TYPE, this.type); if (this.numShards != null) { - await setLabeledInput(page, 'Num shards', String(this.numShards)); + await setLabeledInput(page, HashedPartitionsSpec.NUM_SHARDS, String(this.numShards)); } } } +async function getLabeledInputAsNumber( + page: playwright.Page, + label: string, +): Promise { + const valueString = await getLabeledInput(page, label); + return valueString === '' ? null : Number(valueString); +} + interface HashedPartitionsSpecProps { readonly numShards: number | null; } @@ -63,21 +91,58 @@ interface HashedPartitionsSpecProps { export interface HashedPartitionsSpec extends HashedPartitionsSpecProps {} export class SingleDimPartitionsSpec implements PartitionsSpec { + public static TYPE = 'single_dim'; + private static PARTITION_DIMENSION = 'Partition dimension'; + private static TARGET_ROWS_PER_SEGMENT = 'Target rows per segment'; + private static MAX_ROWS_PER_SEGMENT = 'Max rows per segment'; + readonly type: string; + static async read(page: playwright.Page): Promise { + const partitionDimension = await getLabeledInput( + page, + SingleDimPartitionsSpec.PARTITION_DIMENSION, + ); + const targetRowsPerSegment = await getLabeledInputAsNumber( + page, + SingleDimPartitionsSpec.TARGET_ROWS_PER_SEGMENT, + ); + const maxRowsPerSegment = await getLabeledInputAsNumber( + page, + SingleDimPartitionsSpec.MAX_ROWS_PER_SEGMENT, + ); + return new SingleDimPartitionsSpec({ + partitionDimension, + targetRowsPerSegment, + maxRowsPerSegment, + }); + } + constructor(props: SingleDimPartitionsSpecProps) { Object.assign(this, props); - this.type = 'single_dim'; + this.type = SingleDimPartitionsSpec.TYPE; } async apply(page: playwright.Page): Promise { await selectSuggestibleInput(page, PARTITIONING_TYPE, this.type); - await setLabeledInput(page, 'Partition dimension', this.partitionDimension); + await setLabeledInput( + page, + SingleDimPartitionsSpec.PARTITION_DIMENSION, + this.partitionDimension, + ); if (this.targetRowsPerSegment) { - await setLabeledInput(page, 'Target rows per segment', String(this.targetRowsPerSegment)); + await setLabeledInput( + page, + SingleDimPartitionsSpec.TARGET_ROWS_PER_SEGMENT, + String(this.targetRowsPerSegment), + ); } if (this.maxRowsPerSegment) { - await setLabeledInput(page, 'Max rows per segment', String(this.maxRowsPerSegment)); + await setLabeledInput( + page, + SingleDimPartitionsSpec.MAX_ROWS_PER_SEGMENT, + String(this.maxRowsPerSegment), + ); } } } diff --git a/web-console/e2e-tests/util/playwright.ts b/web-console/e2e-tests/util/playwright.ts index e31a31b4acc..1a421a9a4f7 100644 --- a/web-console/e2e-tests/util/playwright.ts +++ b/web-console/e2e-tests/util/playwright.ts @@ -49,6 +49,13 @@ export async function createPage(browser: playwright.Browser): Promise { + return await page.$eval( + `//*[text()="${label}"]/following-sibling::div//input`, + el => (el as HTMLInputElement).value, + ); +} + export async function setLabeledInput( page: playwright.Page, label: string, diff --git a/web-console/src/views/datasource-view/datasource-view.tsx b/web-console/src/views/datasource-view/datasource-view.tsx index 59688b04a65..9f81a03cb58 100644 --- a/web-console/src/views/datasource-view/datasource-view.tsx +++ b/web-console/src/views/datasource-view/datasource-view.tsx @@ -1209,12 +1209,12 @@ GROUP BY 1`; width: ACTION_COLUMN_WIDTH, filterable: false, Cell: ({ value: datasource, original }) => { - const { unused, rules, compaction } = original; + const { unused, rules, compactionConfig } = original; const datasourceActions = this.getDatasourceActions( datasource, unused, rules, - compaction, + compactionConfig, ); return (