druid/web-console/e2e-tests/component/load-data/data-loader.ts

189 lines
7.0 KiB
TypeScript

/*
* 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 type * as playwright from 'playwright-chromium';
import { clickButton, setLabeledInput, setLabeledTextarea } from '../../util/playwright';
import type { ConfigureSchemaConfig } from './config/configure-schema';
import type { ConfigureTimestampConfig } from './config/configure-timestamp';
import type { PartitionConfig } from './config/partition';
import type { PublishConfig } from './config/publish';
import type { DataConnector } from './data-connector/data-connector';
/**
* Represents load data tab.
*/
export class DataLoader {
private readonly baseUrl: string;
constructor(props: DataLoaderProps) {
Object.assign(this, props);
this.baseUrl = props.unifiedConsoleUrl + '#data-loader';
}
/**
* Execute each step to load data.
*/
async load() {
await this.page.goto(this.baseUrl);
await this.startNewSpecIfNeeded();
await this.start();
await this.connect(this.connector, this.connectValidator);
if (this.connector.needParse) {
await this.parseData();
await this.parseTime(this.configureTimestampConfig);
}
await this.transform();
await this.filter();
await this.configureSchema(this.configureSchemaConfig);
await this.partition(this.partitionConfig);
await this.tune();
await this.publish(this.publishConfig);
await this.editSpec();
}
private async startNewSpecIfNeeded() {
const startNewSpecLocator = this.page.locator(`//*[contains(text(),"Start a new")]`);
if (await startNewSpecLocator.count()) {
await startNewSpecLocator.click();
}
}
private async start() {
const cardSelector = `//*[contains(@class,"bp5-card")][p[contains(text(),"${this.connector.name}")]]`;
await this.page.click(cardSelector);
await clickButton(this.page, 'Connect data');
}
private async connect(connector: DataConnector, validator: (preview: string) => void) {
await connector.connect();
await this.validateConnect(validator);
const next = this.connector.needParse ? 'Parse data' : 'Transform';
await clickButton(this.page, `Next: ${next}`);
}
private async validateConnect(validator: (preview: string) => void) {
const previewSelector = '.raw-lines';
await this.page.waitForSelector(previewSelector);
const preview = await this.page.$eval(previewSelector, el => (el as HTMLTextAreaElement).value);
validator(preview);
}
private async parseData() {
await this.page.waitForSelector('.parse-data-table');
await clickButton(this.page, 'Next: Parse time');
}
private async parseTime(configureTimestampConfig?: ConfigureTimestampConfig) {
await this.page.waitForSelector('.parse-time-table');
if (configureTimestampConfig) {
await this.applyConfigureTimestampConfig(configureTimestampConfig);
}
await clickButton(this.page, 'Next: Transform');
}
private async transform() {
await this.page.waitForSelector('.transform-table');
await clickButton(this.page, 'Next: Filter');
}
private async filter() {
await this.page.waitForSelector('.filter-table');
await clickButton(this.page, 'Next: Configure schema');
}
private async configureSchema(configureSchemaConfig: ConfigureSchemaConfig) {
await this.page.waitForSelector('.schema-table');
await this.applyConfigureSchemaConfig(configureSchemaConfig);
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`);
const rollupChecked = await rollupInput!.evaluate(el => (el as HTMLInputElement).checked);
if (rollupChecked !== configureSchemaConfig.rollup) {
await this.page.click(rollupSelector);
const confirmationDialogSelector = '//*[contains(@class,"bp5-alert-body")]';
await this.page.waitForSelector(confirmationDialogSelector);
await clickButton(this.page, 'Yes');
const statusMessageSelector = '.recipe-toaster';
await this.page.waitForSelector(statusMessageSelector);
await this.page.click(`${statusMessageSelector} button`);
}
}
private async partition(partitionConfig: PartitionConfig) {
await this.page.waitForSelector('div.load-data-view.partition');
await this.applyPartitionConfig(partitionConfig);
await clickButton(this.page, 'Next: Tune');
}
private async applyPartitionConfig(partitionConfig: PartitionConfig) {
await setLabeledInput(this.page, 'Segment granularity', partitionConfig.segmentGranularity);
if (partitionConfig.timeIntervals) {
await setLabeledTextarea(this.page, 'Time intervals', partitionConfig.timeIntervals);
}
if (partitionConfig.partitionsSpec != null) {
await partitionConfig.partitionsSpec.apply(this.page);
}
}
private async tune() {
await this.page.waitForSelector('div.load-data-view.tuning');
await clickButton(this.page, 'Next: Publish');
}
private async publish(publishConfig: PublishConfig) {
await this.page.waitForSelector('div.load-data-view.publish');
await this.applyPublishConfig(publishConfig);
await clickButton(this.page, 'Edit spec');
}
private async applyPublishConfig(publishConfig: PublishConfig) {
if (publishConfig.datasourceName != null) {
await setLabeledInput(this.page, 'Datasource name', publishConfig.datasourceName);
}
}
private async editSpec() {
await this.page.waitForSelector('div.load-data-view.spec');
await clickButton(this.page, 'Submit');
}
}
interface DataLoaderProps {
readonly page: playwright.Page;
readonly unifiedConsoleUrl: string;
readonly connector: DataConnector;
readonly connectValidator: (preview: string) => void;
readonly configureTimestampConfig?: ConfigureTimestampConfig;
readonly configureSchemaConfig: ConfigureSchemaConfig;
readonly partitionConfig: PartitionConfig;
readonly publishConfig: PublishConfig;
}
export interface DataLoader extends DataLoaderProps {}