mirror of https://github.com/apache/druid.git
Support real query cancelling for web console (#11738)
* Support real query cancelling for web console * use uuid for queryId, create isSql reuse variable, and add catch for rejectionhandled promise * remove delete api promise.then() response * slove conflicts * update read me with debug * add degub code to test why CI failed * included a druid extension called druid-testing-tools and it is not build nor loaded by default * remove unuse variable * remove debug log
This commit is contained in:
parent
bc3b038712
commit
f82baf174e
|
@ -115,6 +115,13 @@ The environment variable `DRUID_E2E_TEST_UNIFIED_CONSOLE_PORT` can be used to ta
|
|||
non-default port (i.e., not port `8888`). For example, this environment variable can be used to target the
|
||||
development mode of the web console (started via `npm start`), which runs on port `18081`.
|
||||
|
||||
Like so: `DRUID_E2E_TEST_UNIFIED_CONSOLE_PORT=18081 npm run test-e2e`
|
||||
|
||||
#### Running and debugging a single e2e test using Jest and Playwright
|
||||
|
||||
- Run - `jest --config jest.e2e.config.js e2e-tests/tutorial-batch.spec.ts`.
|
||||
- Debug - `PWDEBUG=console jest --config jest.e2e.config.js e2e-tests/tutorial-batch.spec.ts`.
|
||||
|
||||
## Description of the directory structure
|
||||
|
||||
As part of this directory:
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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 * as playwright from 'playwright-chromium';
|
||||
|
||||
import { QueryOverview } from './component/query/overview';
|
||||
import { saveScreenshotIfError } from './util/debug';
|
||||
import { UNIFIED_CONSOLE_URL } from './util/druid';
|
||||
import { createBrowser, createPage } from './util/playwright';
|
||||
import { waitTillWebConsoleReady } from './util/setup';
|
||||
|
||||
jest.setTimeout(5 * 60 * 1000);
|
||||
|
||||
describe('Cancel query', () => {
|
||||
let browser: playwright.Browser;
|
||||
let page: playwright.Page;
|
||||
|
||||
beforeAll(async () => {
|
||||
await waitTillWebConsoleReady();
|
||||
browser = await createBrowser();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
page = await createPage(browser);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await browser.close();
|
||||
});
|
||||
|
||||
it('delete accepted', async () => {
|
||||
const testName = 'cancel-query';
|
||||
await saveScreenshotIfError(testName, page, async () => {
|
||||
await validateCancelQuery(page);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function validateCancelQuery(page: playwright.Page) {
|
||||
const queryOverview = new QueryOverview(page, UNIFIED_CONSOLE_URL);
|
||||
const query = 'SELECT sleep(40)';
|
||||
const results = await queryOverview.cancelQuery(query);
|
||||
expect(results).toBeDefined();
|
||||
expect(results).toBeGreaterThan(0);
|
||||
expect(results).toStrictEqual(202);
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import * as playwright from 'playwright-chromium';
|
||||
|
||||
import { clickButton, setInput } from '../../util/playwright';
|
||||
import { clickButton, clickText, setInput } from '../../util/playwright';
|
||||
import { extractTable } from '../../util/table';
|
||||
|
||||
/**
|
||||
|
@ -44,4 +44,41 @@ export class QueryOverview {
|
|||
|
||||
return await extractTable(this.page, 'div.query-output div.rt-tr-group', 'div.rt-td');
|
||||
}
|
||||
|
||||
async cancelQuery(query: string): Promise<number> {
|
||||
await this.page.goto(this.baseUrl);
|
||||
await this.page.reload({ waitUntil: 'networkidle' });
|
||||
|
||||
await this.page.waitForSelector('div.query-input textarea');
|
||||
const input = await this.page.$('div.query-input textarea');
|
||||
|
||||
await setInput(input!, query);
|
||||
|
||||
await Promise.all([
|
||||
this.page.waitForRequest(
|
||||
request => request.url().includes('druid/v2') && request.method() === 'POST',
|
||||
),
|
||||
clickButton(this.page, 'Run'),
|
||||
]);
|
||||
|
||||
await this.page.waitForSelector('.cancel-label');
|
||||
|
||||
const [resp] = await Promise.all([
|
||||
this.page.waitForResponse(
|
||||
response => response.url().includes('druid/v2') && response.request().method() === 'DELETE',
|
||||
),
|
||||
|
||||
clickText(this.page, 'Cancel query'),
|
||||
this.page.off(
|
||||
'requestfinished',
|
||||
request => request.url().includes('druid/v2') && request.method() === 'POST',
|
||||
),
|
||||
this.page.off(
|
||||
'requestfinished',
|
||||
request => request.url().includes('druid/v2') && request.method() === 'DELETE',
|
||||
),
|
||||
]);
|
||||
|
||||
return resp.status();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,6 +100,10 @@ export async function clickLabeledButton(
|
|||
await page.click(`//*[text()="${label}"]/following-sibling::div${buttonSelector(text)}`);
|
||||
}
|
||||
|
||||
export async function clickText(page: playwright.Page, text: string): Promise<void> {
|
||||
await page.click(`//*[text()="${text}"]`);
|
||||
}
|
||||
|
||||
export async function selectSuggestibleInput(
|
||||
page: playwright.Page,
|
||||
label: string,
|
||||
|
|
|
@ -59,7 +59,10 @@ function _build_distribution() {
|
|||
&& mvn -Pdist,skip-static-checks,skip-tests -Dmaven.javadoc.skip=true -q -T1C install \
|
||||
&& cd distribution/target \
|
||||
&& tar xzf "apache-druid-$(_get_druid_version)-bin.tar.gz" \
|
||||
&& echo -e "\n\ndruid.server.http.allowedHttpMethods=[\"HEAD\"]" >> apache-druid-$(_get_druid_version)/conf/druid/single-server/micro-quickstart/_common/common.runtime.properties
|
||||
&& cd apache-druid-$(_get_druid_version) \
|
||||
&& java -classpath "lib/*" org.apache.druid.cli.Main tools pull-deps -c org.apache.druid.extensions:druid-testing-tools \
|
||||
&& echo -e "\n\ndruid.extensions.loadList=[\"druid-hdfs-storage\", \"druid-kafka-indexing-service\", \"druid-datasketches\", \"druid-testing-tools\"]" >> conf/druid/single-server/micro-quickstart/_common/common.runtime.properties \
|
||||
&& echo -e "\n\ndruid.server.http.allowedHttpMethods=[\"HEAD\"]" >> conf/druid/single-server/micro-quickstart/_common/common.runtime.properties \
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import * as JSONBig from 'json-bigint-native';
|
|||
import memoizeOne from 'memoize-one';
|
||||
import React, { RefObject } from 'react';
|
||||
import SplitterLayout from 'react-splitter-layout';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { Loader } from '../../components';
|
||||
import { EditContextDialog } from '../../dialogs/edit-context-dialog/edit-context-dialog';
|
||||
|
@ -203,16 +204,33 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
|||
): Promise<QueryResult> => {
|
||||
const { queryString, queryContext, wrapQueryLimit } = queryWithContext;
|
||||
|
||||
const query = QueryView.isJsonLike(queryString) ? Hjson.parse(queryString) : queryString;
|
||||
const isSql = !QueryView.isJsonLike(queryString);
|
||||
|
||||
const query = isSql ? queryString : Hjson.parse(queryString);
|
||||
|
||||
const queryId = uuidv4();
|
||||
|
||||
let context: Record<string, any> | undefined;
|
||||
if (!isEmptyContext(queryContext) || wrapQueryLimit || mandatoryQueryContext) {
|
||||
context = { ...queryContext, ...(mandatoryQueryContext || {}) };
|
||||
|
||||
if (isSql) {
|
||||
context.sqlQueryId = queryId;
|
||||
} else {
|
||||
context.queryId = queryId;
|
||||
}
|
||||
|
||||
if (typeof wrapQueryLimit !== 'undefined') {
|
||||
context.sqlOuterLimit = wrapQueryLimit + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void cancelToken.promise
|
||||
.then(() => {
|
||||
return Api.instance.delete(`/druid/v2${isSql ? '/sql' : ''}/${queryId}`);
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
try {
|
||||
return await queryRunner.runQuery({
|
||||
query,
|
||||
|
|
Loading…
Reference in New Issue