Skip to content
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
2 changes: 2 additions & 0 deletions web-console/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ when the test fails. For example, if `e2e-tests/tutorial-batch.spec.ts` fails, i
Disabling headless mode while running the tests can be helpful. This can be done via the `DRUID_E2E_TEST_HEADLESS`
environment variable, which defaults to `true`.

Like so: `DRUID_E2E_TEST_HEADLESS=false npm run test-e2e`

#### Running against alternate web console

The environment variable `DRUID_E2E_TEST_UNIFIED_CONSOLE_PORT` can be used to target a web console running on a
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Data loader configure timestamp step configuration.
*/
export class ConfigureTimestampConfig {
constructor(props: ConfigureTimestampConfigProps) {
Object.assign(this, props);
}
}

interface ConfigureTimestampConfigProps {
readonly timestampExpression: string;
}

export interface ConfigureTimestampConfig extends ConfigureTimestampConfigProps {}
15 changes: 13 additions & 2 deletions web-console/e2e-tests/component/load-data/data-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import * as playwright from 'playwright-chromium';
import { clickButton, setLabeledInput, setLabeledTextarea } from '../../util/playwright';

import { ConfigureSchemaConfig } from './config/configure-schema';
import { ConfigureTimestampConfig } from './config/configure-timestamp';
import { PartitionConfig } from './config/partition';
import { PublishConfig } from './config/publish';
import { DataConnector } from './data-connector/data-connector';
Expand All @@ -45,7 +46,7 @@ export class DataLoader {
await this.connect(this.connector, this.connectValidator);
if (this.connector.needParse) {
await this.parseData();
await this.parseTime();
await this.parseTime(this.configureTimestampConfig);
}
await this.transform();
await this.filter();
Expand Down Expand Up @@ -81,8 +82,11 @@ export class DataLoader {
await clickButton(this.page, 'Next: Parse time');
}

private async parseTime() {
private async parseTime(configureTimestampConfig?: ConfigureTimestampConfig) {
await this.page.waitForSelector('.parse-time-table');
if (configureTimestampConfig) {
await this.applyConfigureTimestampConfig(configureTimestampConfig);
}
await clickButton(this.page, 'Next: Transform');
}

Expand All @@ -102,6 +106,12 @@ export class DataLoader {
await clickButton(this.page, 'Next: Partition');
}

private async applyConfigureTimestampConfig(configureTimestampConfig: ConfigureTimestampConfig) {
await clickButton(this.page, 'Expression');
await setLabeledInput(this.page, 'Expression', configureTimestampConfig.timestampExpression);
await clickButton(this.page, 'Apply');
}

private async applyConfigureSchemaConfig(configureSchemaConfig: ConfigureSchemaConfig) {
const rollupSelector = '//*[text()="Rollup"]';
const rollupInput = await this.page.$(`${rollupSelector}/input`);
Expand Down Expand Up @@ -161,6 +171,7 @@ interface DataLoaderProps {
readonly unifiedConsoleUrl: string;
readonly connector: DataConnector;
readonly connectValidator: (preview: string) => void;
readonly configureTimestampConfig?: ConfigureTimestampConfig;
readonly configureSchemaConfig: ConfigureSchemaConfig;
readonly partitionConfig: PartitionConfig;
readonly publishConfig: PublishConfig;
Expand Down
43 changes: 20 additions & 23 deletions web-console/e2e-tests/tutorial-batch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,15 @@ import * as playwright from 'playwright-chromium';
import { DatasourcesOverview } from './component/datasources/overview';
import { IngestionOverview } from './component/ingestion/overview';
import { ConfigureSchemaConfig } from './component/load-data/config/configure-schema';
import { SegmentGranularity } from './component/load-data/config/partition';
import { PartitionConfig } from './component/load-data/config/partition';
import { ConfigureTimestampConfig } from './component/load-data/config/configure-timestamp';
import { PartitionConfig, SegmentGranularity } from './component/load-data/config/partition';
import { PublishConfig } from './component/load-data/config/publish';
import { LocalFileDataConnector } from './component/load-data/data-connector/local-file';
import { DataLoader } from './component/load-data/data-loader';
import { QueryOverview } from './component/query/overview';
import { saveScreenshotIfError } from './util/debug';
import { DRUID_EXAMPLES_QUICKSTART_TUTORIAL_DIR } from './util/druid';
import { UNIFIED_CONSOLE_URL } from './util/druid';
import { createBrowser } from './util/playwright';
import { createPage } from './util/playwright';
import { DRUID_EXAMPLES_QUICKSTART_TUTORIAL_DIR, UNIFIED_CONSOLE_URL } from './util/druid';
import { createBrowser, createPage } from './util/playwright';
import { retryIfJestAssertionError } from './util/retry';
import { waitTillWebConsoleReady } from './util/setup';

Expand Down Expand Up @@ -60,26 +58,24 @@ describe('Tutorial: Loading a file', () => {
it('Loads data from local disk', async () => {
const testName = 'load-data-from-local-disk-';
const datasourceName = testName + ALL_SORTS_OF_CHARS + new Date().toISOString();
const dataConnector = new LocalFileDataConnector(page, {
baseDirectory: DRUID_EXAMPLES_QUICKSTART_TUTORIAL_DIR,
fileFilter: 'wikiticker-2015-09-12-sampled.json.gz',
});
const configureSchemaConfig = new ConfigureSchemaConfig({ rollup: false });
const partitionConfig = new PartitionConfig({
segmentGranularity: SegmentGranularity.DAY,
timeIntervals: null,
partitionsSpec: null,
});
const publishConfig = new PublishConfig({ datasourceName: datasourceName });

const dataLoader = new DataLoader({
page: page,
unifiedConsoleUrl: UNIFIED_CONSOLE_URL,
connector: dataConnector,
connector: new LocalFileDataConnector(page, {
baseDirectory: DRUID_EXAMPLES_QUICKSTART_TUTORIAL_DIR,
fileFilter: 'wikiticker-2015-09-12-sampled.json.gz',
}),
connectValidator: validateConnectLocalData,
configureSchemaConfig: configureSchemaConfig,
partitionConfig: partitionConfig,
publishConfig: publishConfig,
configureTimestampConfig: new ConfigureTimestampConfig({
timestampExpression: 'timestamp_parse("time") + 1',
}),
configureSchemaConfig: new ConfigureSchemaConfig({ rollup: false }),
partitionConfig: new PartitionConfig({
segmentGranularity: SegmentGranularity.DAY,
timeIntervals: null,
partitionsSpec: null,
}),
publishConfig: new PublishConfig({ datasourceName: datasourceName }),
});

await saveScreenshotIfError(testName, page, async () => {
Expand Down Expand Up @@ -176,7 +172,7 @@ async function validateQuery(page: playwright.Page, datasourceName: string) {
expect(results).toBeDefined();
expect(results.length).toBeGreaterThan(0);
expect(results[0]).toStrictEqual([
/* __time */ '2015-09-12T00:46:58.771Z',
/* __time */ '2015-09-12T00:46:58.772Z',
/* added */ '36',
/* channel */ '#en.wikipedia',
/* cityName */ 'null',
Expand All @@ -195,6 +191,7 @@ async function validateQuery(page: playwright.Page, datasourceName: string) {
/* page */ 'Talk:Oswald Tilghman',
/* regionIsoCode */ 'null',
/* regionName */ 'null',
/* time */ '2015-09-12T00:46:58.771Z',
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the e2e test and realized/was faced with this interesting side-effect:
If you specify the time column via the timestampSpec then that column gets excluded from the dimension auto-detection system. If instead you use a transform to construct a __time dimension then (obviously) the component dimensions do not get removed from the auto detection. In this case I set __time to be timestamp_parse("time") + 1 and now clicking though all the defaults yields an extra field, the original time column.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this cause a problem? It's documented behavior (item 3): https://druid.apache.org/docs/latest/ingestion/index.html#inclusions-and-exclusions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it is not a problem. Just something that took me by surprise. Simply because I did not put 1 + 1 together. Maybe there should be some docs of this inside the web console.

/* user */ 'GELongstreet',
]);
}
45 changes: 45 additions & 0 deletions web-console/src/druid-models/transform-spec.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { getDimensionNamesFromTransforms } from './transform-spec';

describe('transform-spec', () => {
describe('getDimensionNamesFromTransforms', () => {
it('does not return the __time column', () => {
expect(
getDimensionNamesFromTransforms([
{
type: 'expression',
name: 'fooPage',
expression: "concat('foo' + page)",
},
{
name: '__time',
type: 'expression',
expression: "timestamp_shift(timestamp_parse(timestamp), 'P3Y', 1)",
},
{
type: 'expression',
name: 'barPage',
expression: "concat('foo' + page)",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

heh, barPage concats 'foo'?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is also verifying that running this function on transforms that have the same expression does not does not cause an asynchronous desynchronization of the DOM rendering thread with the memory management subsystem thereby causing memory liquefaction, which will cause a memory leek and water damage the browser.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch! we don't want our Druid bits to get too corroded from any water spillover into the query request packets, else we might one day wake up to find all our code has been converted to rust.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😐

},
]),
).toEqual(['fooPage', 'barPage']);
});
});
});
10 changes: 8 additions & 2 deletions web-console/src/druid-models/transform-spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import React from 'react';
import { ExternalLink, Field } from '../components';
import { getLink } from '../links';

import { TIME_COLUMN } from './timestamp-spec';

export interface TransformSpec {
transforms?: Transform[];
filter?: Record<string, any>;
Expand Down Expand Up @@ -92,14 +94,18 @@ export function getTimestampExpressionFields(transforms: Transform[]): Field<Tra
export function addTimestampTransform(transforms: Transform[]): Transform[] {
return [
{
name: '__time',
name: TIME_COLUMN,
type: 'expression',
expression: '',
},
].concat(transforms);
}

export function removeTimestampTransform(transforms: Transform[]): Transform[] | undefined {
const newTransforms = transforms.filter(transform => transform.name !== '__time');
const newTransforms = transforms.filter(transform => transform.name !== TIME_COLUMN);
return newTransforms.length ? newTransforms : undefined;
}

export function getDimensionNamesFromTransforms(transforms: Transform[]): string[] {
return transforms.map(t => t.name).filter(n => n !== TIME_COLUMN);
}
5 changes: 3 additions & 2 deletions web-console/src/utils/sampler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import * as JSONBig from 'json-bigint-native';

import {
DimensionsSpec,
getDimensionNamesFromTransforms,
getSpecType,
getTimestampSchema,
IngestionSpec,
Expand Down Expand Up @@ -464,7 +465,7 @@ export async function sampleForTransform(
sampleResponse: sampleResponseHack,
ignoreTimeColumn: true,
columnOrder: [TIME_COLUMN].concat(inputFormatColumns),
}).concat(transforms.map(t => t.name)),
}).concat(getDimensionNamesFromTransforms(transforms)),
);
}

Expand Down Expand Up @@ -523,7 +524,7 @@ export async function sampleForFilter(
sampleResponse: sampleResponseHack,
ignoreTimeColumn: true,
columnOrder: [TIME_COLUMN].concat(inputFormatColumns),
}).concat(transforms.map(t => t.name)),
}).concat(getDimensionNamesFromTransforms(transforms)),
);
}

Expand Down