mirror of https://github.com/apache/druid.git
Web console: Switch to ESLint (#11142)
* Initial eslint config * I guess eslint sorts underscores differently * Trim curlies (in jsx) * Re-organize rules * Use consistent quote props * Restructure eslint rules as additions/overrides to recommended configs * Fix the 'recommended' stuff * Add prefer-readonly * Add prefer-object-spread * Prettify * Add eslint-plugin-react-hooks * Switch to eslint-plugin-simple-sort-order So much better * Add no-extraneous-dependencies * ban-tslint-comment for funzies * If we enabled no-shadow, we'd probably want this option * Not prefer-for-of * no-confusing-void-expression, no-confusing-non-null-assertion * Add some no-unnecessary-* rules * non-nullable-type-assertion-style! * prefer-includes * Reorganize * prefer-things * switch-exhaustiveness-check * We don't need the jsdoc plugin, prettier has our backs * Remove a useless rule * Drop TSLint and (temporarily) awesome-code-style * Removing Object.assign revealed a type issue * Bring back awesome-code-style for sasslint config * Disable react/jsx-no-target-blank * Add prettify-check script * Add license to eslint config * Format readme * Update README for eslint, IDE settings * Add 'autofix' script * Switch to @awesome-code-style
This commit is contained in:
parent
57ff1f9cdb
commit
9745d9e1c3
|
@ -16,14 +16,24 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export function useRenderSpy(componentName: string, props: Record<string, any>) {
|
||||
console.log(`Render on ${componentName}`);
|
||||
const propKeys: string[] = Object.keys(props).sort();
|
||||
for (const key of propKeys) {
|
||||
useEffect(() => {
|
||||
console.log(`${key} changed`);
|
||||
}, [(props as any)[key]]);
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
},
|
||||
extends: ['@awesome-code-style'],
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'header/header': [
|
||||
2,
|
||||
'block',
|
||||
{ pattern: 'Licensed to the Apache Software Foundation \\(ASF\\).+' },
|
||||
],
|
||||
},
|
||||
};
|
|
@ -1,17 +1,4 @@
|
|||
{
|
||||
"plugins": [
|
||||
"stylelint-scss"
|
||||
],
|
||||
"defaultSeverity": "error",
|
||||
"extends": "stylelint-config-recommended-scss",
|
||||
"rules": {
|
||||
"indentation": 2,
|
||||
"at-rule-no-unknown": null,
|
||||
"scss/at-rule-no-unknown": true,
|
||||
"scss/at-import-no-partial-leading-underscore": true,
|
||||
"scss/dollar-variable-colon-space-after": "always",
|
||||
"scss/dollar-variable-colon-space-before": "never",
|
||||
"no-descending-specificity": null,
|
||||
"no-missing-end-of-source-newline": true
|
||||
}
|
||||
"extends": "@awesome-code-style/stylelint-config",
|
||||
"rules": {}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ This is the Druid web console that servers as a data management interface for Dr
|
|||
3. Run `npm run compile` to compile the scss files (this usually needs to be done only once)
|
||||
4. Run `npm start` will start in development mode and will proxy druid requests to `localhost:8888`
|
||||
|
||||
|
||||
**Note:** you can provide an environment variable to proxy to a different Druid host like so: `druid_host=1.2.3.4:8888 npm start`
|
||||
**Note:** you can provide an environment variable use webpack-bundle-analyzer as a plugin in the build script or like so: `BUNDLE_ANALYZER_PLUGIN='TRUE' npm start`
|
||||
|
||||
|
@ -42,25 +41,48 @@ To try the console in (say) coordinator mode you could run it as such:
|
|||
|
||||
You should use a TypeScript friendly IDE (such as [WebStorm](https://www.jetbrains.com/webstorm/), or [VS Code](https://code.visualstudio.com/)) to develop the web console.
|
||||
|
||||
The console relies on [tslint](https://palantir.github.io/tslint/), [sass-lint](https://github.com/sasstools/sass-lint), and [prettier](https://prettier.io/) to enforce the code style.
|
||||
The console relies on [eslint](https://eslint.org) (and various plugins), [sass-lint](https://github.com/sasstools/sass-lint), and [prettier](https://prettier.io/) to enforce code style. If you are going to do any non-trivial development you should set up your IDE to automatically lint and fix your code as you make changes.
|
||||
|
||||
If you are going to do any non-trivial development you should set up file watchers in your IDE to automatically fix your code as you type.
|
||||
#### Configuring WebStorm
|
||||
|
||||
If you do not set up auto file watchers then even a trivial change such as a typo fix might draw the ire of the code style enforcement (it might require some lines to be re-wrapped).
|
||||
If you find yourself in that position you should run on or more of:
|
||||
- **Preferences | Languages & Frameworks | JavaScript | Code Quality Tools | ESLint**
|
||||
- Select "Automatic ESLint Configuration"
|
||||
- Check "Run eslint --fix on save"
|
||||
|
||||
- `npm run tslint-fix`
|
||||
- `npm run sasslint-fix`
|
||||
- `npm run prettify`
|
||||
- **Preferences | Languages & Frameworks | JavaScript | Prettier**
|
||||
- Set "Run for files" to `{**/*,*}.{js,ts,jsx,tsx,css,scss}`
|
||||
- Check "On code reformat"
|
||||
- Check "On save"
|
||||
|
||||
To get your code into an acceptable state.
|
||||
#### Configuring VS Code
|
||||
- Install `dbaeumer.vscode-eslint` extension
|
||||
- Install `esbenp.prettier-vscode` extension
|
||||
- Open User Settings (JSON) and set the following:
|
||||
```json
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
}
|
||||
```
|
||||
|
||||
#### Auto-fixing manually
|
||||
It is also possible to auto-fix and format code without making IDE changes by running the following script:
|
||||
|
||||
- `npm run autofix` — run code linters and formatter
|
||||
|
||||
You could also run fixers individually:
|
||||
|
||||
- `npm run eslint-fix` — run code linter and fix issues
|
||||
- `npm run sasslint-fix` — run style linter and fix issues
|
||||
- `npm run prettify` — reformat code and styles
|
||||
|
||||
### Updating the list of license files
|
||||
|
||||
If you change the dependencies of the console in any way please run `script/licenses` (from the web-console directory).
|
||||
It will analyze the changes and update the `../licenses` file as needed.
|
||||
|
||||
Please be conscious of not introducing dependencies on packages with Apache incompatible licenses.
|
||||
Please be conscious of not introducing dependencies on packages with Apache incompatible licenses.
|
||||
|
||||
### Running end-to-end tests
|
||||
|
||||
|
@ -93,7 +115,6 @@ 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`.
|
||||
|
||||
|
||||
## Description of the directory structure
|
||||
|
||||
As part of this directory:
|
||||
|
|
|
@ -23,19 +23,19 @@ module.exports = function (api) {
|
|||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
"useBuiltIns": "entry",
|
||||
"corejs": 3,
|
||||
"forceAllTransforms": true,
|
||||
"targets": {
|
||||
"ie": "11"
|
||||
}
|
||||
}
|
||||
]
|
||||
useBuiltIns: 'entry',
|
||||
corejs: 3,
|
||||
forceAllTransforms: true,
|
||||
targets: {
|
||||
ie: '11',
|
||||
},
|
||||
},
|
||||
],
|
||||
];
|
||||
const plugins = [];
|
||||
|
||||
return {
|
||||
presets,
|
||||
plugins
|
||||
plugins,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
@ -17,6 +17,6 @@
|
|||
*/
|
||||
|
||||
window.consoleConfig = {
|
||||
"exampleManifestsUrl": "https://druid.apache.org/data/example-manifests-v2.tsv"
|
||||
exampleManifestsUrl: 'https://druid.apache.org/data/example-manifests-v2.tsv',
|
||||
/* future configs may go here */
|
||||
};
|
||||
|
|
|
@ -24,11 +24,12 @@ import { Datasource } from './component/datasources/datasource';
|
|||
import { DatasourcesOverview } from './component/datasources/overview';
|
||||
import { HashedPartitionsSpec } from './component/load-data/config/partition';
|
||||
import { saveScreenshotIfError } from './util/debug';
|
||||
import { DRUID_EXAMPLES_QUICKSTART_TUTORIAL_DIR } from './util/druid';
|
||||
import { UNIFIED_CONSOLE_URL } from './util/druid';
|
||||
import { runIndexTask } from './util/druid';
|
||||
import { createBrowser } from './util/playwright';
|
||||
import { createPage } from './util/playwright';
|
||||
import {
|
||||
DRUID_EXAMPLES_QUICKSTART_TUTORIAL_DIR,
|
||||
runIndexTask,
|
||||
UNIFIED_CONSOLE_URL,
|
||||
} from './util/druid';
|
||||
import { createBrowser, createPage } from './util/playwright';
|
||||
import { retryIfJestAssertionError } from './util/retry';
|
||||
import { waitTillWebConsoleReady } from './util/setup';
|
||||
|
||||
|
@ -154,6 +155,6 @@ async function waitForCompaction(
|
|||
|
||||
async function getNumSegment(page: playwright.Page, datasourceName: string): Promise<number> {
|
||||
const datasource = await getDatasource(page, datasourceName);
|
||||
const currNumSegmentString = datasource!.availability.match(/(\d+)/)![0];
|
||||
const currNumSegmentString = /(\d+)/.exec(datasource.availability)![0];
|
||||
return Number(currNumSegmentString);
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ export class DatasourcesOverview {
|
|||
}
|
||||
|
||||
const editActions = await this.page.$$('span[icon=wrench]');
|
||||
editActions[index].click();
|
||||
await editActions[index].click();
|
||||
await this.waitForPopupMenu();
|
||||
}
|
||||
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable max-classes-per-file */
|
||||
|
||||
import * as playwright from 'playwright-chromium';
|
||||
|
||||
import { getLabeledInput, selectSuggestibleInput, setLabeledInput } from '../../../util/playwright';
|
||||
|
||||
/* tslint:disable max-classes-per-file */
|
||||
|
||||
/**
|
||||
* Possible values for partition step segment granularity.
|
||||
*/
|
||||
|
@ -52,7 +52,7 @@ export async function readPartitionSpec(page: playwright.Page): Promise<Partitio
|
|||
|
||||
export class HashedPartitionsSpec implements PartitionsSpec {
|
||||
public static TYPE = 'hashed';
|
||||
private static NUM_SHARDS = 'Num shards';
|
||||
private static readonly NUM_SHARDS = 'Num shards';
|
||||
|
||||
readonly type: string;
|
||||
|
||||
|
@ -90,9 +90,9 @@ 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';
|
||||
private static readonly PARTITION_DIMENSION = 'Partition dimension';
|
||||
private static readonly TARGET_ROWS_PER_SEGMENT = 'Target rows per segment';
|
||||
private static readonly MAX_ROWS_PER_SEGMENT = 'Max rows per segment';
|
||||
|
||||
readonly type: string;
|
||||
|
||||
|
|
|
@ -20,8 +20,7 @@ import * as playwright from 'playwright-chromium';
|
|||
|
||||
import { setLabeledInput } from '../../../util/playwright';
|
||||
|
||||
import { clickApplyButton } from './data-connector';
|
||||
import { DataConnector } from './data-connector';
|
||||
import { clickApplyButton, DataConnector } from './data-connector';
|
||||
|
||||
/**
|
||||
* Local file connector for data loader input data.
|
||||
|
@ -29,7 +28,7 @@ import { DataConnector } from './data-connector';
|
|||
export class LocalFileDataConnector implements DataConnector {
|
||||
readonly name: string;
|
||||
readonly needParse: boolean;
|
||||
private page: playwright.Page;
|
||||
private readonly page: playwright.Page;
|
||||
|
||||
constructor(page: playwright.Page, props: LocalFileDataConnectorProps) {
|
||||
Object.assign(this, props);
|
||||
|
|
|
@ -20,8 +20,7 @@ import * as playwright from 'playwright-chromium';
|
|||
|
||||
import { setLabeledInput } from '../../../util/playwright';
|
||||
|
||||
import { clickApplyButton } from './data-connector';
|
||||
import { DataConnector } from './data-connector';
|
||||
import { clickApplyButton, DataConnector } from './data-connector';
|
||||
|
||||
/**
|
||||
* Reindexing connector for data loader input data.
|
||||
|
@ -29,7 +28,7 @@ import { DataConnector } from './data-connector';
|
|||
export class ReindexDataConnector implements DataConnector {
|
||||
readonly name: string;
|
||||
readonly needParse: boolean;
|
||||
private page: playwright.Page;
|
||||
private readonly page: playwright.Page;
|
||||
|
||||
constructor(page: playwright.Page, props: ReindexDataConnectorProps) {
|
||||
Object.assign(this, props);
|
||||
|
|
|
@ -34,7 +34,7 @@ export class DataLoader {
|
|||
|
||||
constructor(props: DataLoaderProps) {
|
||||
Object.assign(this, props);
|
||||
this.baseUrl = props.unifiedConsoleUrl! + '#load-data';
|
||||
this.baseUrl = props.unifiedConsoleUrl + '#load-data';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,7 +74,7 @@ export class DataLoader {
|
|||
const previewSelector = '.raw-lines';
|
||||
await this.page.waitForSelector(previewSelector);
|
||||
const preview = await this.page.$eval(previewSelector, el => (el as HTMLTextAreaElement).value);
|
||||
validator(preview!);
|
||||
validator(preview);
|
||||
}
|
||||
|
||||
private async parseData() {
|
||||
|
|
|
@ -22,18 +22,21 @@ 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 { PartitionConfig } from './component/load-data/config/partition';
|
||||
import { SegmentGranularity } from './component/load-data/config/partition';
|
||||
import { SingleDimPartitionsSpec } from './component/load-data/config/partition';
|
||||
import {
|
||||
PartitionConfig,
|
||||
SegmentGranularity,
|
||||
SingleDimPartitionsSpec,
|
||||
} from './component/load-data/config/partition';
|
||||
import { PublishConfig } from './component/load-data/config/publish';
|
||||
import { ReindexDataConnector } from './component/load-data/data-connector/reindex';
|
||||
import { DataLoader } from './component/load-data/data-loader';
|
||||
import { saveScreenshotIfError } from './util/debug';
|
||||
import { DRUID_EXAMPLES_QUICKSTART_TUTORIAL_DIR } from './util/druid';
|
||||
import { UNIFIED_CONSOLE_URL } from './util/druid';
|
||||
import { runIndexTask } from './util/druid';
|
||||
import { createBrowser } from './util/playwright';
|
||||
import { createPage } from './util/playwright';
|
||||
import {
|
||||
DRUID_EXAMPLES_QUICKSTART_TUTORIAL_DIR,
|
||||
runIndexTask,
|
||||
UNIFIED_CONSOLE_URL,
|
||||
} from './util/druid';
|
||||
import { createBrowser, createPage } from './util/playwright';
|
||||
import { retryIfJestAssertionError } from './util/retry';
|
||||
import { waitTillWebConsoleReady } from './util/setup';
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ export async function retryIfJestAssertionError(
|
|||
maxTries = 60,
|
||||
) {
|
||||
let i = 0;
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
try {
|
||||
await callback();
|
||||
|
|
|
@ -17,8 +17,7 @@
|
|||
*/
|
||||
|
||||
import { UNIFIED_CONSOLE_URL } from './druid';
|
||||
import { createBrowser } from './playwright';
|
||||
import { createPage } from './playwright';
|
||||
import { createBrowser, createPage } from './playwright';
|
||||
|
||||
export async function waitTillWebConsoleReady() {
|
||||
const browser = await createBrowser();
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
*/
|
||||
|
||||
module.exports = {
|
||||
"preset": "ts-jest",
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"tsconfig": "./tsconfig.test.json"
|
||||
}
|
||||
preset: 'ts-jest',
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: './tsconfig.test.json',
|
||||
},
|
||||
},
|
||||
"testEnvironment": "jsdom",
|
||||
testEnvironment: 'jsdom',
|
||||
};
|
||||
|
|
|
@ -19,7 +19,5 @@
|
|||
const common = require('./jest.common.config');
|
||||
|
||||
module.exports = Object.assign(common, {
|
||||
"testMatch": [
|
||||
"**/?(*.)+(spec).ts?(x)"
|
||||
]
|
||||
testMatch: ['**/?(*.)+(spec).ts?(x)'],
|
||||
});
|
||||
|
|
|
@ -19,16 +19,10 @@
|
|||
const common = require('./jest.common.config');
|
||||
|
||||
module.exports = Object.assign(common, {
|
||||
"moduleNameMapper": {
|
||||
"\\.s?css$": "identity-obj-proxy"
|
||||
moduleNameMapper: {
|
||||
'\\.s?css$': 'identity-obj-proxy',
|
||||
},
|
||||
"snapshotSerializers": [
|
||||
"enzyme-to-json/serializer"
|
||||
],
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>src/setup-tests.ts"
|
||||
],
|
||||
"testMatch": [
|
||||
"**/src/**/?(*.)+(spec).(ts|tsx)"
|
||||
],
|
||||
snapshotSerializers: ['enzyme-to-json/serializer'],
|
||||
setupFilesAfterEnv: ['<rootDir>src/setup-tests.ts'],
|
||||
testMatch: ['**/src/**/?(*.)+(spec).(ts|tsx)'],
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -27,35 +27,29 @@
|
|||
"<rootDir>src/setup-tests.ts"
|
||||
]
|
||||
},
|
||||
"prettier": {
|
||||
"trailingComma": "all",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"endOfLine": "lf",
|
||||
"arrowParens": "avoid"
|
||||
},
|
||||
"prettier": "@awesome-code-style/prettier-config",
|
||||
"scripts": {
|
||||
"compile": "./script/build",
|
||||
"pretest": "./script/build",
|
||||
"jest": "jest --config jest.unit.config.js src",
|
||||
"test-base": "npm run tslint && npm run sasslint && npm run jest",
|
||||
"test-base": "npm run eslint && npm run sasslint && npm run prettify-check && npm run jest",
|
||||
"test": "npm run test-base -- --silent 2>&1",
|
||||
"test-ci": "npm run test-base -- --coverage",
|
||||
"test-e2e": "jest --config jest.e2e.config.js e2e-tests",
|
||||
"codecov": "codecov --disable=gcov -p ..",
|
||||
"coverage": "jest --coverage src",
|
||||
"update-snapshots": "jest -u",
|
||||
"tslint": "./node_modules/.bin/tslint -c tslint.json --project tsconfig.json --formatters-dir ./node_modules/awesome-code-style/formatter '{src,e2e-tests}/**/*.ts?(x)'",
|
||||
"tslint-fix": "npm run tslint -- --fix",
|
||||
"tslint-changed-only": "git diff --diff-filter=ACMR --cached --name-only | grep -E \\.tsx\\?$ | xargs ./node_modules/.bin/tslint -c tslint.json --project tsconfig.json --formatters-dir ./node_modules/awesome-code-style/formatter",
|
||||
"tslint-fix-changed-only": "npm run tslint-changed-only -- --fix",
|
||||
"sasslint": "./node_modules/.bin/stylelint --config sasslint.json 'src/**/*.scss'",
|
||||
"autofix": "npm run eslint-fix && npm run sasslint-fix && npm run prettify",
|
||||
"eslint": "eslint '{src,e2e-tests}/**/*.ts?(x)'",
|
||||
"eslint-fix": "npm run eslint -- --fix",
|
||||
"eslint-changed-only": "git diff --diff-filter=ACMR --cached --name-only | grep -E \\.tsx\\?$ | xargs ./node_modules/.bin/eslint",
|
||||
"eslint-fix-changed-only": "npm run eslint-changed-only -- --fix",
|
||||
"sasslint": "./node_modules/.bin/stylelint 'src/**/*.scss'",
|
||||
"sasslint-fix": "npm run sasslint -- --fix",
|
||||
"sasslint-changed-only": "git diff --diff-filter=ACMR --name-only | grep -E \\.scss$ | xargs ./node_modules/.bin/stylelint --config sasslint.json",
|
||||
"sasslint-changed-only": "git diff --diff-filter=ACMR --name-only | grep -E \\.scss$ | xargs ./node_modules/.bin/stylelint",
|
||||
"sasslint-fix-changed-only": "npm run sasslint-changed-only -- --fix",
|
||||
"prettify": "prettier --write '{src,e2e-tests}/**/*.{ts,tsx,scss}'",
|
||||
"prettify": "prettier --write '{src,e2e-tests}/**/*.{ts,tsx,scss}' './*.js'",
|
||||
"prettify-check": "prettier --check '{src,e2e-tests}/**/*.{ts,tsx,scss}' './*.js'",
|
||||
"generate-licenses-file": "license-checker --production --json --out licenses.json",
|
||||
"check-licenses": "license-checker --production --onlyAllow 'Apache-1.1;Apache-2.0;BSD-2-Clause;BSD-3-Clause;0BSD;MIT;CC0-1.0' --summary",
|
||||
"start": "webpack serve --hot --open"
|
||||
|
@ -94,6 +88,9 @@
|
|||
"tslib": "^2.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@awesome-code-style/eslint-config": "^3.0.0",
|
||||
"@awesome-code-style/prettier-config": "^3.0.0",
|
||||
"@awesome-code-style/stylelint-config": "^3.0.0",
|
||||
"@babel/core": "^7.13.15",
|
||||
"@babel/preset-env": "^7.13.15",
|
||||
"@testing-library/react": "^8.0.9",
|
||||
|
@ -118,14 +115,22 @@
|
|||
"@types/react-splitter-layout": "^3.0.0",
|
||||
"@types/react-table": "6.8.5",
|
||||
"@types/uuid": "^7.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
||||
"autoprefixer": "^9.8.6",
|
||||
"awesome-code-style": "^2.1.0",
|
||||
"babel-loader": "^8.2.2",
|
||||
"codecov": "^3.6.1",
|
||||
"css-loader": "^5.2.1",
|
||||
"enzyme": "^3.10.0",
|
||||
"enzyme-adapter-react-16": "^1.15.1",
|
||||
"enzyme-to-json": "^3.4.3",
|
||||
"eslint": "^7.24.0",
|
||||
"eslint-config-prettier": "^8.2.0",
|
||||
"eslint-plugin-header": "^3.1.1",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-react": "^7.23.2",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||
"eslint-plugin-unicorn": "^30.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
|
@ -140,14 +145,10 @@
|
|||
"sass-loader": "^11.0.1",
|
||||
"style-loader": "^2.0.0",
|
||||
"stylelint": "^13.12.0",
|
||||
"stylelint-config-recommended-scss": "^4.2.0",
|
||||
"stylelint-scss": "^3.19.0",
|
||||
"stylus": "^0.54.7",
|
||||
"ts-jest": "^26.5.5",
|
||||
"ts-loader": "^8.1.0",
|
||||
"ts-node": "^8.4.1",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-loader": "^3.5.4",
|
||||
"typescript": "^4.2.4",
|
||||
"uuid": "^7.0.2",
|
||||
"webpack": "^5.33.2",
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"extends": "awesome-code-style/sasslint.json",
|
||||
"rules": {}
|
||||
}
|
|
@ -19,6 +19,5 @@
|
|||
import 'brace'; // Import Ace editor and all the sub components used in the app
|
||||
import 'brace/ext/language_tools';
|
||||
import 'brace/theme/solarized_dark';
|
||||
|
||||
import '../ace-modes/dsql';
|
||||
import '../ace-modes/hjson';
|
||||
|
|
|
@ -41,7 +41,7 @@ interface ReactTableCustomPaginationProps {
|
|||
}
|
||||
|
||||
interface ReactTableCustomPaginationState {
|
||||
page: string | number;
|
||||
page: '' | number;
|
||||
}
|
||||
|
||||
export class ReactTableCustomPagination extends React.PureComponent<
|
||||
|
@ -56,8 +56,10 @@ export class ReactTableCustomPagination extends React.PureComponent<
|
|||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: ReactTableCustomPaginationProps) {
|
||||
this.setState({ page: nextProps.page });
|
||||
static getDerivedStateFromProps(nextProps: ReactTableCustomPaginationProps) {
|
||||
return {
|
||||
page: nextProps.page,
|
||||
};
|
||||
}
|
||||
|
||||
getSafePage = (page: any) => {
|
||||
|
@ -126,13 +128,11 @@ export class ReactTableCustomPagination extends React.PureComponent<
|
|||
type={this.state.page === '' ? 'text' : 'number'}
|
||||
onChange={e => {
|
||||
const val: string = e.target.value;
|
||||
const page: number = parseInt(val, 10) - 1;
|
||||
if (val === '') {
|
||||
return this.setState({ page: val });
|
||||
}
|
||||
this.setState({ page: this.getSafePage(page) });
|
||||
this.setState({
|
||||
page: val === '' ? val : this.getSafePage(parseInt(val, 10) - 1),
|
||||
});
|
||||
}}
|
||||
value={this.state.page === '' ? '' : (this.state.page as number) + 1}
|
||||
value={this.state.page === '' ? '' : this.state.page + 1}
|
||||
onBlur={this.applyPage}
|
||||
onKeyPress={e => {
|
||||
if (e.which === 13 || e.keyCode === 13) {
|
||||
|
|
|
@ -42,7 +42,7 @@ export function bootstrapReactTable() {
|
|||
NoDataComponent: NoData,
|
||||
FilterComponent: makeTextFilter(),
|
||||
PaginationComponent: ReactTableCustomPagination,
|
||||
AggregatedComponent: (opt: any) => {
|
||||
AggregatedComponent: function Aggregated(opt: any) {
|
||||
const { subRows, column } = opt;
|
||||
const previewValues = subRows
|
||||
.filter((d: any) => typeof d[column.id] !== 'undefined')
|
||||
|
|
|
@ -26,8 +26,8 @@ describe('array input', () => {
|
|||
const arrayInput = (
|
||||
<ArrayInput
|
||||
values={['apple', 'banana', 'pear']}
|
||||
className={'test'}
|
||||
placeholder={'test'}
|
||||
className="test"
|
||||
placeholder="test"
|
||||
onChange={() => {}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -33,7 +33,7 @@ export interface ArrayInputProps {
|
|||
|
||||
export const ArrayInput = React.memo(function ArrayInput(props: ArrayInputProps) {
|
||||
const { className, placeholder, large, disabled, intent } = props;
|
||||
const [stringValue, setStringValue] = useState();
|
||||
const [stringValue, setStringValue] = useState<string>();
|
||||
|
||||
const handleChange = (e: any) => {
|
||||
const { onChange } = props;
|
||||
|
@ -54,7 +54,7 @@ export const ArrayInput = React.memo(function ArrayInput(props: ArrayInputProps)
|
|||
return (
|
||||
<TextArea
|
||||
className={className}
|
||||
value={stringValue || (props.values || []).join(', ')}
|
||||
value={stringValue ?? props.values?.join(', ') ?? ''}
|
||||
onChange={handleChange}
|
||||
placeholder={placeholder}
|
||||
large={large}
|
||||
|
|
|
@ -155,7 +155,7 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent
|
|||
};
|
||||
}
|
||||
|
||||
private fieldChange = (field: Field<T>, newValue: any) => {
|
||||
private readonly fieldChange = (field: Field<T>, newValue: any) => {
|
||||
const { model } = this.props;
|
||||
if (!model) return;
|
||||
|
||||
|
@ -181,7 +181,7 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent
|
|||
this.modelChange(newModel);
|
||||
};
|
||||
|
||||
private modelChange = (newModel: T) => {
|
||||
private readonly modelChange = (newModel: T) => {
|
||||
const { globalAdjustment, fields, onChange, model } = this.props;
|
||||
|
||||
// Delete things that are not defined now (but were defined prior to the change)
|
||||
|
@ -406,7 +406,7 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent
|
|||
}
|
||||
}
|
||||
|
||||
private renderField = (field: Field<T>) => {
|
||||
private readonly renderField = (field: Field<T>) => {
|
||||
const { model } = this.props;
|
||||
if (!model) return;
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@ describe('clearable-input', () => {
|
|||
it('matches snapshot', () => {
|
||||
const centerMessage = (
|
||||
<ClearableInput
|
||||
className={'testClassName'}
|
||||
value={'testValue'}
|
||||
placeholder={'testPlaceholder'}
|
||||
className="testClassName"
|
||||
value="testValue"
|
||||
placeholder="testPlaceholder"
|
||||
onChange={() => {}}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -32,7 +32,7 @@ jest.mock('../../hooks', () => {
|
|||
|
||||
describe('DatasourceColumnsTable', () => {
|
||||
function makeDatasourceColumnsTable() {
|
||||
return <DatasourceColumnsTable datasourceId={'test'} downloadFilename={'test'} />;
|
||||
return <DatasourceColumnsTable datasourceId="test" downloadFilename="test" />;
|
||||
}
|
||||
|
||||
it('matches snapshot on init', () => {
|
||||
|
|
|
@ -24,7 +24,7 @@ import { ExternalLink } from './external-link';
|
|||
describe('external link', () => {
|
||||
it('matches snapshot', () => {
|
||||
const externalLink = (
|
||||
<ExternalLink href={'http://test/'}>
|
||||
<ExternalLink href="http://test/">
|
||||
<div>hello world</div>
|
||||
</ExternalLink>
|
||||
);
|
||||
|
|
|
@ -35,7 +35,7 @@ export const FormGroupWithInfo = React.memo(function FormGroupWithInfo(
|
|||
const { label, info, inlineInfo, children } = props;
|
||||
|
||||
const popover = (
|
||||
<Popover content={info} position="left-bottom" boundary={'viewport'}>
|
||||
<Popover content={info} position="left-bottom" boundary="viewport">
|
||||
<Icon icon={IconNames.INFO_SIGN} iconSize={14} />
|
||||
</Popover>
|
||||
);
|
||||
|
|
|
@ -25,7 +25,7 @@ import { HeaderBar } from './header-bar';
|
|||
|
||||
describe('header bar', () => {
|
||||
it('matches snapshot', () => {
|
||||
const headerBar = shallow(<HeaderBar active={'load-data'} capabilities={Capabilities.FULL} />);
|
||||
const headerBar = shallow(<HeaderBar active="load-data" capabilities={Capabilities.FULL} />);
|
||||
expect(headerBar).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,6 +24,7 @@ export * from './braced-text/braced-text';
|
|||
export * from './center-message/center-message';
|
||||
export * from './clearable-input/clearable-input';
|
||||
export * from './external-link/external-link';
|
||||
export * from './form-json-selector/form-json-selector';
|
||||
export * from './header-bar/header-bar';
|
||||
export * from './highlight-text/highlight-text';
|
||||
export * from './json-collapse/json-collapse';
|
||||
|
@ -41,4 +42,3 @@ export * from './table-cell/table-cell';
|
|||
export * from './table-column-selector/table-column-selector';
|
||||
export * from './timed-button/timed-button';
|
||||
export * from './view-control-bar/view-control-bar';
|
||||
export * from './form-json-selector/form-json-selector';
|
||||
|
|
|
@ -26,8 +26,8 @@ describe('interval calendar component', () => {
|
|||
it('matches snapshot', () => {
|
||||
const intervalInput = (
|
||||
<IntervalInput
|
||||
interval={'2010-01-01/2020-01-01'}
|
||||
placeholder={'2010-01-01/2020-01-01'}
|
||||
interval="2010-01-01/2020-01-01"
|
||||
placeholder="2010-01-01/2020-01-01"
|
||||
onValueChange={() => {}}
|
||||
intent={Intent.PRIMARY}
|
||||
/>
|
||||
|
|
|
@ -69,10 +69,10 @@ export const IntervalInput = React.memo(function IntervalInput(props: IntervalIn
|
|||
rightElement={
|
||||
<div>
|
||||
<Popover
|
||||
popoverClassName={'calendar'}
|
||||
popoverClassName="calendar"
|
||||
content={
|
||||
<DateRangePicker
|
||||
timePrecision={'second'}
|
||||
timePrecision="second"
|
||||
value={parseInterval(interval)}
|
||||
contiguousCalendarMonths={false}
|
||||
onChange={(selectedRange: DateRange) => {
|
||||
|
|
|
@ -25,7 +25,7 @@ import { JsonCollapse } from './json-collapse';
|
|||
describe('JsonCollapse', () => {
|
||||
it('matches snapshot', () => {
|
||||
const jsonCollapse = shallow(
|
||||
<JsonCollapse buttonText={'test'} stringValue={JSONBig.stringify({ name: 'test' })} />,
|
||||
<JsonCollapse buttonText="test" stringValue={JSONBig.stringify({ name: 'test' })} />,
|
||||
);
|
||||
expect(jsonCollapse).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -37,7 +37,7 @@ export function extractRowColumnFromHjsonError(
|
|||
// Message would be something like:
|
||||
// `Found '}' where a key name was expected at line 26,7`
|
||||
// Use this to extract the row and column (subtract 1) and jump the cursor to the right place on click
|
||||
const m = error.message.match(/line (\d+),(\d+)/);
|
||||
const m = /line (\d+),(\d+)/.exec(error.message);
|
||||
if (!m) return;
|
||||
|
||||
return { row: Number(m[1]) - 1, column: Number(m[2]) - 1 };
|
||||
|
@ -89,7 +89,7 @@ export const JsonInput = React.memo(function JsonInput(props: JsonInputProps) {
|
|||
value,
|
||||
stringified: stringifyJson(value),
|
||||
});
|
||||
}, [value]);
|
||||
}, [value]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const internalValueError = internalValue.error;
|
||||
return (
|
||||
|
|
|
@ -23,7 +23,7 @@ import { Loader } from './loader';
|
|||
|
||||
describe('loader', () => {
|
||||
it('matches snapshot', () => {
|
||||
const loader = <Loader loadingText={'test'} />;
|
||||
const loader = <Loader loadingText="test" />;
|
||||
const { container } = render(loader);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -23,7 +23,7 @@ import { LookupValuesTable } from './lookup-values-table';
|
|||
|
||||
describe('rule editor', () => {
|
||||
it('matches snapshot', () => {
|
||||
const showJson = <LookupValuesTable lookupId={'test'} downloadFilename={'test'} />;
|
||||
const showJson = <LookupValuesTable lookupId="test" downloadFilename="test" />;
|
||||
const { container } = render(showJson);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -37,13 +37,11 @@ export const NumericInputWithDefault = React.memo(function NumericInputWithDefau
|
|||
value={effectiveValue}
|
||||
onValueChange={(valueAsNumber, valueAsString, inputElement) => {
|
||||
setHasChanged(true);
|
||||
if (!onValueChange) return;
|
||||
return onValueChange(valueAsNumber, valueAsString, inputElement);
|
||||
onValueChange?.(valueAsNumber, valueAsString, inputElement);
|
||||
}}
|
||||
onBlur={e => {
|
||||
setHasChanged(false);
|
||||
if (!onBlur) return;
|
||||
return onBlur(e);
|
||||
onBlur?.(e);
|
||||
}}
|
||||
{...rest}
|
||||
/>
|
||||
|
|
|
@ -51,10 +51,10 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
|
|||
const [isOpen, setIsOpen] = useState(true);
|
||||
|
||||
function removeTier(key: string) {
|
||||
const newTierReplicants = Object.assign({}, rule.tieredReplicants);
|
||||
const newTierReplicants = { ...rule.tieredReplicants };
|
||||
delete newTierReplicants[key];
|
||||
|
||||
const newRule = Object.assign({}, rule, { tieredReplicants: newTierReplicants });
|
||||
const newRule = { ...rule, tieredReplicants: newTierReplicants };
|
||||
onChange(newRule);
|
||||
}
|
||||
|
||||
|
@ -162,9 +162,7 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
|
|||
<ControlGroup>
|
||||
<HTMLSelect
|
||||
value={rule.type}
|
||||
onChange={(e: any) =>
|
||||
onChange(RuleUtil.changeRuleType(rule, e.target.value as any))
|
||||
}
|
||||
onChange={(e: any) => onChange(RuleUtil.changeRuleType(rule, e.target.value))}
|
||||
>
|
||||
{RuleUtil.TYPES.map(type => {
|
||||
return (
|
||||
|
@ -200,9 +198,7 @@ export const RuleEditor = React.memo(function RuleEditor(props: RuleEditorProps)
|
|||
{RuleUtil.hasInterval(rule) && (
|
||||
<InputGroup
|
||||
value={rule.interval || ''}
|
||||
onChange={(e: any) =>
|
||||
onChange(RuleUtil.changeInterval(rule, e.target.value as any))
|
||||
}
|
||||
onChange={(e: any) => onChange(RuleUtil.changeInterval(rule, e.target.value))}
|
||||
placeholder="2010-01-01/2020-01-01"
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -20,8 +20,7 @@ import { render } from '@testing-library/react';
|
|||
import { mount } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import { QueryManager } from '../../utils';
|
||||
import { Capabilities } from '../../utils';
|
||||
import { Capabilities, QueryManager } from '../../utils';
|
||||
|
||||
import { SegmentTimeline } from './segment-timeline';
|
||||
|
||||
|
@ -87,6 +86,7 @@ class MockDataQueryManager extends QueryManager<
|
|||
|
||||
constructor() {
|
||||
super({
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
processQuery: async ({ timeSpan }) => {
|
||||
this.queryTimeSpan = timeSpan;
|
||||
},
|
||||
|
|
|
@ -22,8 +22,7 @@ import { scaleLinear, scaleTime } from 'd3-scale';
|
|||
import React from 'react';
|
||||
|
||||
import { Api } from '../../singletons';
|
||||
import { Capabilities } from '../../utils';
|
||||
import { formatBytes, queryDruidSql, QueryManager, uniq } from '../../utils/index';
|
||||
import { Capabilities, formatBytes, queryDruidSql, QueryManager, uniq } from '../../utils';
|
||||
import { StackedBarChart } from '../../visualization/stacked-bar-chart';
|
||||
import { Loader } from '../loader/loader';
|
||||
|
||||
|
@ -138,9 +137,9 @@ export class SegmentTimeline extends React.PureComponent<
|
|||
total: segmentSize,
|
||||
};
|
||||
} else {
|
||||
const countDataEntry = countData[day][datasource];
|
||||
const countDataEntry: number | undefined = countData[day][datasource];
|
||||
countData[day][datasource] = count + (countDataEntry === undefined ? 0 : countDataEntry);
|
||||
const sizeDataEntry = sizeData[day][datasource];
|
||||
const sizeDataEntry: number | undefined = sizeData[day][datasource];
|
||||
sizeData[day][datasource] = segmentSize + (sizeDataEntry === undefined ? 0 : sizeDataEntry);
|
||||
countData[day].total += count;
|
||||
sizeData[day].total += segmentSize;
|
||||
|
@ -219,8 +218,12 @@ export class SegmentTimeline extends React.PureComponent<
|
|||
return singleDatasourceData;
|
||||
}
|
||||
|
||||
private dataQueryManager: QueryManager<{ capabilities: Capabilities; timeSpan: number }, any>;
|
||||
private chartMargin = { top: 20, right: 10, bottom: 20, left: 10 };
|
||||
private readonly dataQueryManager: QueryManager<
|
||||
{ capabilities: Capabilities; timeSpan: number },
|
||||
any
|
||||
>;
|
||||
|
||||
private readonly chartMargin = { top: 20, right: 10, bottom: 20, left: 10 };
|
||||
|
||||
constructor(props: SegmentTimelineProps) {
|
||||
super(props);
|
||||
|
@ -342,8 +345,7 @@ ORDER BY "start" DESC`;
|
|||
prevProps.chartHeight !== this.props.chartHeight
|
||||
) {
|
||||
const scales: BarChartScales | undefined = this.calculateScales();
|
||||
let dataToRender: BarUnitData[] | undefined;
|
||||
dataToRender = activeDatasource
|
||||
const dataToRender: BarUnitData[] | undefined = activeDatasource
|
||||
? singleDatasourceData
|
||||
? singleDatasourceData[activeDataType][activeDatasource]
|
||||
: undefined
|
||||
|
@ -456,7 +458,7 @@ ORDER BY "start" DESC`;
|
|||
if (error) {
|
||||
return (
|
||||
<div>
|
||||
<span className={'no-data-text'}>Error when loading data: {error.message}</span>
|
||||
<span className="no-data-text">Error when loading data: {error.message}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -464,7 +466,7 @@ ORDER BY "start" DESC`;
|
|||
if (xScale === null || yScale === null) {
|
||||
return (
|
||||
<div>
|
||||
<span className={'no-data-text'}>Error when calculating scales</span>
|
||||
<span className="no-data-text">Error when calculating scales</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -472,7 +474,7 @@ ORDER BY "start" DESC`;
|
|||
if (data![activeDataType].length === 0) {
|
||||
return (
|
||||
<div>
|
||||
<span className={'no-data-text'}>No data available for the time span selected</span>
|
||||
<span className="no-data-text">No data available for the time span selected</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -483,7 +485,7 @@ ORDER BY "start" DESC`;
|
|||
) {
|
||||
return (
|
||||
<div>
|
||||
<span className={'no-data-text'}>
|
||||
<span className="no-data-text">
|
||||
No data available for <i>{activeDatasource}</i>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -517,20 +519,20 @@ ORDER BY "start" DESC`;
|
|||
const { datasources, activeDataType, activeDatasource, timeSpan } = this.state;
|
||||
|
||||
return (
|
||||
<div className={'segment-timeline app-view'}>
|
||||
<div className="segment-timeline app-view">
|
||||
{this.renderStackedBarChart()}
|
||||
<div className={'side-control'}>
|
||||
<div className="side-control">
|
||||
<FormGroup>
|
||||
<RadioGroup
|
||||
onChange={(e: any) => this.setState({ activeDataType: e.target.value })}
|
||||
selectedValue={activeDataType}
|
||||
>
|
||||
<Radio label={'Total size'} value={'sizeData'} />
|
||||
<Radio label={'Segment count'} value={'countData'} />
|
||||
<Radio label="Total size" value="sizeData" />
|
||||
<Radio label="Segment count" value="countData" />
|
||||
</RadioGroup>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup label={'Datasource:'}>
|
||||
<FormGroup label="Datasource:">
|
||||
<HTMLSelect
|
||||
onChange={(e: any) =>
|
||||
this.setState({
|
||||
|
@ -540,7 +542,7 @@ ORDER BY "start" DESC`;
|
|||
value={activeDatasource == null ? 'all' : activeDatasource}
|
||||
fill
|
||||
>
|
||||
<option value={'all'}>Show all</option>
|
||||
<option value="all">Show all</option>
|
||||
{datasources.map(d => {
|
||||
return (
|
||||
<option key={d} value={d}>
|
||||
|
@ -551,7 +553,7 @@ ORDER BY "start" DESC`;
|
|||
</HTMLSelect>
|
||||
</FormGroup>
|
||||
|
||||
<FormGroup label={'Period:'}>
|
||||
<FormGroup label="Period:">
|
||||
<HTMLSelect
|
||||
onChange={(e: any) => this.onTimeSpanChange(e.target.value)}
|
||||
value={timeSpan}
|
||||
|
|
|
@ -23,7 +23,7 @@ import { ShowHistory } from './show-history';
|
|||
|
||||
describe('show history', () => {
|
||||
it('matches snapshot', () => {
|
||||
const showJson = <ShowHistory endpoint={'test'} downloadFilename={'test'} />;
|
||||
const showJson = <ShowHistory endpoint="test" downloadFilename="test" />;
|
||||
const { container } = render(showJson);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -67,19 +67,13 @@ export const ShowHistory = React.memo(function ShowHistory(props: ShowHistoryPro
|
|||
endpoint={endpoint}
|
||||
/>
|
||||
}
|
||||
panelClassName={'panel'}
|
||||
panelClassName="panel"
|
||||
/>
|
||||
));
|
||||
|
||||
return (
|
||||
<div className="show-history">
|
||||
<Tabs
|
||||
animate
|
||||
renderActiveTabPanelOnly
|
||||
vertical
|
||||
className={'tab-area'}
|
||||
defaultSelectedTabId={0}
|
||||
>
|
||||
<Tabs animate renderActiveTabPanelOnly vertical className="tab-area" defaultSelectedTabId={0}>
|
||||
{versions}
|
||||
<Tabs.Expander />
|
||||
</Tabs>
|
||||
|
|
|
@ -23,7 +23,7 @@ import { ShowJson } from './show-json';
|
|||
|
||||
describe('rule editor', () => {
|
||||
it('matches snapshot', () => {
|
||||
const showJson = <ShowJson endpoint={'test'} downloadFilename={'test'} />;
|
||||
const showJson = <ShowJson endpoint="test" downloadFilename="test" />;
|
||||
const { container } = render(showJson);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -24,7 +24,7 @@ import { ShowLog } from './show-log';
|
|||
describe('show log', () => {
|
||||
it('describe show log', () => {
|
||||
const showLog = (
|
||||
<ShowLog status={'RUNNING'} endpoint={'/druid/index/test/log'} downloadFilename={'test'} />
|
||||
<ShowLog status="RUNNING" endpoint="/druid/index/test/log" downloadFilename="test" />
|
||||
);
|
||||
const { container } = render(showLog);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
|
|
|
@ -50,8 +50,8 @@ export interface ShowLogState {
|
|||
export class ShowLog extends React.PureComponent<ShowLogProps, ShowLogState> {
|
||||
static CHECK_INTERVAL = 2500;
|
||||
|
||||
private showLogQueryManager: QueryManager<null, string>;
|
||||
private log = React.createRef<HTMLTextAreaElement>();
|
||||
private readonly showLogQueryManager: QueryManager<null, string>;
|
||||
private readonly log = React.createRef<HTMLTextAreaElement>();
|
||||
private interval: number | undefined;
|
||||
|
||||
constructor(props: ShowLogProps, context: any) {
|
||||
|
@ -123,7 +123,7 @@ export class ShowLog extends React.PureComponent<ShowLogProps, ShowLogState> {
|
|||
delete this.interval;
|
||||
}
|
||||
|
||||
private handleCheckboxChange = () => {
|
||||
private readonly handleCheckboxChange = () => {
|
||||
const { tail } = this.state;
|
||||
|
||||
const nextTail = !tail;
|
||||
|
|
|
@ -23,7 +23,7 @@ import { ShowValue } from './show-value';
|
|||
|
||||
describe('rule editor', () => {
|
||||
it('matches snapshot', () => {
|
||||
const showJson = <ShowValue endpoint={'test'} downloadFilename={'test'} />;
|
||||
const showJson = <ShowValue endpoint="test" downloadFilename="test" />;
|
||||
const { container } = render(showJson);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -83,7 +83,7 @@ export const SuggestibleInput = React.memo(function SuggestibleInput(props: Sugg
|
|||
rightElement={
|
||||
suggestions && (
|
||||
<Popover
|
||||
boundary={'window'}
|
||||
boundary="window"
|
||||
content={
|
||||
<Menu>
|
||||
{suggestions.map(suggestion => {
|
||||
|
|
|
@ -36,7 +36,7 @@ jest.mock('../../hooks', () => {
|
|||
|
||||
describe('SupervisorStatisticsTable', () => {
|
||||
function makeSupervisorStatisticsTable() {
|
||||
return <SupervisorStatisticsTable supervisorId="sup-id" downloadFilename={'test'} />;
|
||||
return <SupervisorStatisticsTable supervisorId="sup-id" downloadFilename="test" />;
|
||||
}
|
||||
|
||||
it('matches snapshot on init', () => {
|
||||
|
|
|
@ -27,7 +27,7 @@ describe('TimedButton', () => {
|
|||
<TimedButton
|
||||
delays={[{ label: 'timeValue', delay: 1000 }]}
|
||||
onRefresh={() => null}
|
||||
label={'Select delay'}
|
||||
label="Select delay"
|
||||
defaultDelay={1000}
|
||||
/>,
|
||||
);
|
||||
|
|
|
@ -29,7 +29,7 @@ export const WarningChecklist = React.memo(function WarningChecklist(props: Warn
|
|||
const [checked, setChecked] = useState<Record<string, boolean>>({});
|
||||
|
||||
function doCheck(check: string) {
|
||||
const newChecked = Object.assign({}, checked);
|
||||
const newChecked = { ...checked };
|
||||
newChecked[check] = !newChecked[check];
|
||||
setChecked(newChecked);
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ export class ConsoleApplication extends React.PureComponent<
|
|||
ConsoleApplicationProps,
|
||||
ConsoleApplicationState
|
||||
> {
|
||||
private capabilitiesQueryManager: QueryManager<null, Capabilities>;
|
||||
private readonly capabilitiesQueryManager: QueryManager<null, Capabilities>;
|
||||
|
||||
static shownNotifications() {
|
||||
AppToaster.show({
|
||||
|
@ -119,47 +119,47 @@ export class ConsoleApplication extends React.PureComponent<
|
|||
}, 50);
|
||||
}
|
||||
|
||||
private goToLoadData = (supervisorId?: string, taskId?: string) => {
|
||||
private readonly goToLoadData = (supervisorId?: string, taskId?: string) => {
|
||||
if (taskId) this.taskId = taskId;
|
||||
if (supervisorId) this.supervisorId = supervisorId;
|
||||
window.location.hash = 'load-data';
|
||||
this.resetInitialsWithDelay();
|
||||
};
|
||||
|
||||
private goToDatasources = (datasource: string) => {
|
||||
private readonly goToDatasources = (datasource: string) => {
|
||||
this.datasource = datasource;
|
||||
window.location.hash = 'datasources';
|
||||
this.resetInitialsWithDelay();
|
||||
};
|
||||
|
||||
private goToSegments = (datasource: string, onlyUnavailable = false) => {
|
||||
private readonly goToSegments = (datasource: string, onlyUnavailable = false) => {
|
||||
this.datasource = datasource;
|
||||
this.onlyUnavailable = onlyUnavailable;
|
||||
window.location.hash = 'segments';
|
||||
this.resetInitialsWithDelay();
|
||||
};
|
||||
|
||||
private goToIngestionWithTaskGroupId = (taskGroupId?: string, openDialog?: string) => {
|
||||
private readonly goToIngestionWithTaskGroupId = (taskGroupId?: string, openDialog?: string) => {
|
||||
this.taskGroupId = taskGroupId;
|
||||
if (openDialog) this.openDialog = openDialog;
|
||||
window.location.hash = 'ingestion';
|
||||
this.resetInitialsWithDelay();
|
||||
};
|
||||
|
||||
private goToIngestionWithDatasource = (datasource?: string, openDialog?: string) => {
|
||||
private readonly goToIngestionWithDatasource = (datasource?: string, openDialog?: string) => {
|
||||
this.datasource = datasource;
|
||||
if (openDialog) this.openDialog = openDialog;
|
||||
window.location.hash = 'ingestion';
|
||||
this.resetInitialsWithDelay();
|
||||
};
|
||||
|
||||
private goToQuery = (initQuery: string) => {
|
||||
private readonly goToQuery = (initQuery: string) => {
|
||||
this.initQuery = initQuery;
|
||||
window.location.hash = 'query';
|
||||
this.resetInitialsWithDelay();
|
||||
};
|
||||
|
||||
private wrapInViewContainer = (
|
||||
private readonly wrapInViewContainer = (
|
||||
active: HeaderActiveTab,
|
||||
el: JSX.Element,
|
||||
classType: 'normal' | 'narrow-pad' | 'thin' = 'normal',
|
||||
|
@ -174,12 +174,12 @@ export class ConsoleApplication extends React.PureComponent<
|
|||
);
|
||||
};
|
||||
|
||||
private wrappedHomeView = () => {
|
||||
private readonly wrappedHomeView = () => {
|
||||
const { capabilities } = this.state;
|
||||
return this.wrapInViewContainer(null, <HomeView capabilities={capabilities} />);
|
||||
};
|
||||
|
||||
private wrappedLoadDataView = () => {
|
||||
private readonly wrappedLoadDataView = () => {
|
||||
const { exampleManifestsUrl } = this.props;
|
||||
|
||||
return this.wrapInViewContainer(
|
||||
|
@ -194,7 +194,7 @@ export class ConsoleApplication extends React.PureComponent<
|
|||
);
|
||||
};
|
||||
|
||||
private wrappedQueryView = () => {
|
||||
private readonly wrappedQueryView = () => {
|
||||
const { defaultQueryContext, mandatoryQueryContext } = this.props;
|
||||
|
||||
return this.wrapInViewContainer(
|
||||
|
@ -208,7 +208,7 @@ export class ConsoleApplication extends React.PureComponent<
|
|||
);
|
||||
};
|
||||
|
||||
private wrappedDatasourcesView = () => {
|
||||
private readonly wrappedDatasourcesView = () => {
|
||||
const { capabilities } = this.state;
|
||||
return this.wrapInViewContainer(
|
||||
'datasources',
|
||||
|
@ -222,7 +222,7 @@ export class ConsoleApplication extends React.PureComponent<
|
|||
);
|
||||
};
|
||||
|
||||
private wrappedSegmentsView = () => {
|
||||
private readonly wrappedSegmentsView = () => {
|
||||
const { capabilities } = this.state;
|
||||
return this.wrapInViewContainer(
|
||||
'segments',
|
||||
|
@ -235,7 +235,7 @@ export class ConsoleApplication extends React.PureComponent<
|
|||
);
|
||||
};
|
||||
|
||||
private wrappedIngestionView = () => {
|
||||
private readonly wrappedIngestionView = () => {
|
||||
const { capabilities } = this.state;
|
||||
return this.wrapInViewContainer(
|
||||
'ingestion',
|
||||
|
@ -251,7 +251,7 @@ export class ConsoleApplication extends React.PureComponent<
|
|||
);
|
||||
};
|
||||
|
||||
private wrappedServicesView = () => {
|
||||
private readonly wrappedServicesView = () => {
|
||||
const { capabilities } = this.state;
|
||||
return this.wrapInViewContainer(
|
||||
'services',
|
||||
|
@ -263,7 +263,7 @@ export class ConsoleApplication extends React.PureComponent<
|
|||
);
|
||||
};
|
||||
|
||||
private wrappedLookupsView = () => {
|
||||
private readonly wrappedLookupsView = () => {
|
||||
return this.wrapInViewContainer('lookups', <LookupsView />);
|
||||
};
|
||||
|
||||
|
|
|
@ -29,9 +29,9 @@ describe('async action dialog', () => {
|
|||
return Promise.resolve();
|
||||
}}
|
||||
onClose={() => {}}
|
||||
confirmButtonText={'test'}
|
||||
successText={'test'}
|
||||
failText={'test'}
|
||||
confirmButtonText="test"
|
||||
successText="test"
|
||||
failText="test"
|
||||
/>
|
||||
);
|
||||
render(asyncActionDialog);
|
||||
|
|
|
@ -28,7 +28,7 @@ describe('CompactionDialog', () => {
|
|||
onClose={() => {}}
|
||||
onSave={() => {}}
|
||||
onDelete={() => {}}
|
||||
datasource={'test1'}
|
||||
datasource="test1"
|
||||
compactionConfig={undefined}
|
||||
/>,
|
||||
);
|
||||
|
@ -41,7 +41,7 @@ describe('CompactionDialog', () => {
|
|||
onClose={() => {}}
|
||||
onSave={() => {}}
|
||||
onDelete={() => {}}
|
||||
datasource={'test1'}
|
||||
datasource="test1"
|
||||
compactionConfig={{
|
||||
dataSource: 'test1',
|
||||
tuningConfig: { partitionsSpec: { type: 'dynamic' } },
|
||||
|
@ -57,7 +57,7 @@ describe('CompactionDialog', () => {
|
|||
onClose={() => {}}
|
||||
onSave={() => {}}
|
||||
onDelete={() => {}}
|
||||
datasource={'test1'}
|
||||
datasource="test1"
|
||||
compactionConfig={{
|
||||
dataSource: 'test1',
|
||||
tuningConfig: { partitionsSpec: { type: 'hashed' } },
|
||||
|
@ -73,7 +73,7 @@ describe('CompactionDialog', () => {
|
|||
onClose={() => {}}
|
||||
onSave={() => {}}
|
||||
onDelete={() => {}}
|
||||
datasource={'test1'}
|
||||
datasource="test1"
|
||||
compactionConfig={{
|
||||
dataSource: 'test1',
|
||||
tuningConfig: { partitionsSpec: { type: 'single_dim' } },
|
||||
|
|
|
@ -20,7 +20,7 @@ import { Button, Classes, Dialog, Intent } from '@blueprintjs/core';
|
|||
import React, { useState } from 'react';
|
||||
|
||||
import { AutoForm, FormJsonSelector, FormJsonTabs, JsonInput } from '../../components';
|
||||
import { CompactionConfig, COMPACTION_CONFIG_FIELDS } from '../../druid-models';
|
||||
import { COMPACTION_CONFIG_FIELDS, CompactionConfig } from '../../druid-models';
|
||||
|
||||
import './compaction-dialog.scss';
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import { Intent } from '@blueprintjs/core';
|
|||
import { IconNames } from '@blueprintjs/icons';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { SnitchDialog } from '..';
|
||||
import {
|
||||
AutoForm,
|
||||
ExternalLink,
|
||||
|
@ -28,11 +27,12 @@ import {
|
|||
FormJsonTabs,
|
||||
JsonInput,
|
||||
} from '../../components';
|
||||
import { CoordinatorDynamicConfig, COORDINATOR_DYNAMIC_CONFIG_FIELDS } from '../../druid-models';
|
||||
import { COORDINATOR_DYNAMIC_CONFIG_FIELDS, CoordinatorDynamicConfig } from '../../druid-models';
|
||||
import { useQueryManager } from '../../hooks';
|
||||
import { getLink } from '../../links';
|
||||
import { Api, AppToaster } from '../../singletons';
|
||||
import { getDruidErrorMessage } from '../../utils';
|
||||
import { SnitchDialog } from '..';
|
||||
|
||||
import './coordinator-dynamic-config-dialog.scss';
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ export const EditContextDialog = React.memo(function EditContextDialog(
|
|||
}
|
||||
|
||||
return (
|
||||
<Dialog className="edit-context-dialog" isOpen onClose={onClose} title={'Edit query context'}>
|
||||
<Dialog className="edit-context-dialog" isOpen onClose={onClose} title="Edit query context">
|
||||
<TextArea value={queryContextString} onChange={handleTextChange} autoFocus />
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
{error && (
|
||||
|
@ -82,10 +82,10 @@ export const EditContextDialog = React.memo(function EditContextDialog(
|
|||
{error}
|
||||
</Callout>
|
||||
)}
|
||||
<div className={'edit-context-dialog-buttons'}>
|
||||
<Button text={'Close'} onClick={onClose} />
|
||||
<div className="edit-context-dialog-buttons">
|
||||
<Button text="Close" onClick={onClose} />
|
||||
<Button
|
||||
text={'Save'}
|
||||
text="Save"
|
||||
intent={Intent.PRIMARY}
|
||||
disabled={Boolean(error)}
|
||||
onClick={
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
*/
|
||||
|
||||
export * from './about-dialog/about-dialog';
|
||||
export * from './doctor-dialog/doctor-dialog';
|
||||
export * from './async-action-dialog/async-action-dialog';
|
||||
export * from './compaction-dialog/compaction-dialog';
|
||||
export * from './coordinator-dynamic-config-dialog/coordinator-dynamic-config-dialog';
|
||||
export * from './doctor-dialog/doctor-dialog';
|
||||
export * from './history-dialog/history-dialog';
|
||||
export * from './lookup-edit-dialog/lookup-edit-dialog';
|
||||
export * from './overlord-dynamic-config-dialog/overlord-dynamic-config-dialog';
|
||||
|
|
|
@ -28,9 +28,9 @@ describe('LookupEditDialog', () => {
|
|||
onClose={() => {}}
|
||||
onSubmit={() => {}}
|
||||
onChange={() => {}}
|
||||
lookupName={'test'}
|
||||
lookupTier={'test'}
|
||||
lookupVersion={'test'}
|
||||
lookupName="test"
|
||||
lookupTier="test"
|
||||
lookupVersion="test"
|
||||
lookupSpec={{ type: 'map', map: { a: 1 } }}
|
||||
isEdit={false}
|
||||
allLookupTiers={['__default', 'alt-tier']}
|
||||
|
|
|
@ -27,9 +27,8 @@ import {
|
|||
} from '@blueprintjs/core';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { AutoForm, JsonInput } from '../../components';
|
||||
import { FormJsonSelector, FormJsonTabs } from '../../components';
|
||||
import { isLookupInvalid, LookupSpec, LOOKUP_FIELDS } from '../../druid-models';
|
||||
import { AutoForm, FormJsonSelector, FormJsonTabs, JsonInput } from '../../components';
|
||||
import { isLookupInvalid, LOOKUP_FIELDS, LookupSpec } from '../../druid-models';
|
||||
|
||||
import './lookup-edit-dialog.scss';
|
||||
|
||||
|
|
|
@ -20,13 +20,13 @@ import { Intent } from '@blueprintjs/core';
|
|||
import { IconNames } from '@blueprintjs/icons';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { SnitchDialog } from '..';
|
||||
import { AutoForm, ExternalLink } from '../../components';
|
||||
import { OverlordDynamicConfig, OVERLORD_DYNAMIC_CONFIG_FIELDS } from '../../druid-models';
|
||||
import { OVERLORD_DYNAMIC_CONFIG_FIELDS, OverlordDynamicConfig } from '../../druid-models';
|
||||
import { useQueryManager } from '../../hooks';
|
||||
import { getLink } from '../../links';
|
||||
import { Api, AppToaster } from '../../singletons';
|
||||
import { getDruidErrorMessage } from '../../utils';
|
||||
import { SnitchDialog } from '..';
|
||||
|
||||
import './overlord-dynamic-config-dialog.scss';
|
||||
|
||||
|
|
|
@ -52,8 +52,8 @@ export const QueryHistoryDialog = React.memo(function QueryHistoryDialog(
|
|||
id={index}
|
||||
key={index}
|
||||
title={record.version}
|
||||
panel={<TextArea readOnly value={record.queryString} className={'text-area'} />}
|
||||
panelClassName={'panel'}
|
||||
panel={<TextArea readOnly value={record.queryString} className="text-area" />}
|
||||
panelClassName="panel"
|
||||
/>
|
||||
));
|
||||
|
||||
|
@ -62,7 +62,7 @@ export const QueryHistoryDialog = React.memo(function QueryHistoryDialog(
|
|||
animate
|
||||
renderActiveTabPanelOnly
|
||||
vertical
|
||||
className={'tab-area'}
|
||||
className="tab-area"
|
||||
selectedTabId={activeTab}
|
||||
onChange={(t: number) => setActiveTab(t)}
|
||||
>
|
||||
|
|
|
@ -26,7 +26,7 @@ describe('query plan dialog', () => {
|
|||
const queryPlanDialog = (
|
||||
<QueryPlanDialog
|
||||
setQueryString={() => null}
|
||||
explainResult={'test'}
|
||||
explainResult="test"
|
||||
explainError={undefined}
|
||||
onClose={() => {}}
|
||||
/>
|
||||
|
|
|
@ -25,7 +25,7 @@ describe('retention dialog', () => {
|
|||
it('matches snapshot', () => {
|
||||
const retentionDialog = (
|
||||
<RetentionDialog
|
||||
datasource={'test-datasource'}
|
||||
datasource="test-datasource"
|
||||
rules={[
|
||||
{
|
||||
period: 'P1000Y',
|
||||
|
|
|
@ -20,13 +20,13 @@ import { Button, Divider, FormGroup } from '@blueprintjs/core';
|
|||
import { IconNames } from '@blueprintjs/icons';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { SnitchDialog } from '..';
|
||||
import { ExternalLink, RuleEditor } from '../../components';
|
||||
import { useQueryManager } from '../../hooks';
|
||||
import { getLink } from '../../links';
|
||||
import { Api } from '../../singletons';
|
||||
import { swapElements } from '../../utils';
|
||||
import { Rule, RuleUtil } from '../../utils/load-rule';
|
||||
import { SnitchDialog } from '..';
|
||||
|
||||
import './retention-dialog.scss';
|
||||
|
||||
|
|
|
@ -26,9 +26,7 @@ describe('clipboard dialog', () => {
|
|||
const compactionDialog = (
|
||||
<ShowValueDialog
|
||||
onClose={() => {}}
|
||||
str={
|
||||
'Bot: Automatska zamjena teksta (-[[Administrativna podjela Meksika|Admin]] +[[Administrativna podjela Meksika|Admi]])'
|
||||
}
|
||||
str="Bot: Automatska zamjena teksta (-[[Administrativna podjela Meksika|Admin]] +[[Administrativna podjela Meksika|Admi]])"
|
||||
/>
|
||||
);
|
||||
render(compactionDialog);
|
||||
|
|
|
@ -45,8 +45,8 @@ export const ShowValueDialog = React.memo(function ShowValueDialog(props: ShowVa
|
|||
<Dialog className="show-value-dialog" isOpen onClose={onClose} title="Full value">
|
||||
<TextArea value={str} />
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button icon={IconNames.DUPLICATE} text={'Copy'} onClick={handleCopy} />
|
||||
<Button text={'Close'} intent={Intent.PRIMARY} onClick={onClose} />
|
||||
<Button icon={IconNames.DUPLICATE} text="Copy" onClick={handleCopy} />
|
||||
<Button text="Close" intent={Intent.PRIMARY} onClick={onClose} />
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
|
|
|
@ -179,7 +179,7 @@ export class SnitchDialog extends React.PureComponent<SnitchDialogProps, SnitchD
|
|||
if (showFinalStep) return this.renderFinalStep();
|
||||
if (showHistory) return this.renderHistoryDialog();
|
||||
|
||||
const propsClone: any = Object.assign({}, this.props);
|
||||
const propsClone: any = { ...this.props };
|
||||
propsClone.className = classNames('snitch-dialog', propsClone.className);
|
||||
return (
|
||||
<Dialog isOpen {...propsClone} canOutsideClickClose={false}>
|
||||
|
|
|
@ -23,7 +23,7 @@ import { SpecDialog } from './spec-dialog';
|
|||
|
||||
describe('spec dialog', () => {
|
||||
it('matches snapshot no initSpec', () => {
|
||||
const specDialog = <SpecDialog onSubmit={() => {}} onClose={() => {}} title={'test'} />;
|
||||
const specDialog = <SpecDialog onSubmit={() => {}} onClose={() => {}} title="test" />;
|
||||
render(specDialog);
|
||||
expect(document.body.lastChild).toMatchSnapshot();
|
||||
});
|
||||
|
@ -34,7 +34,7 @@ describe('spec dialog', () => {
|
|||
initSpec={{ type: 'some-spec' }}
|
||||
onSubmit={() => {}}
|
||||
onClose={() => {}}
|
||||
title={'test'}
|
||||
title="test"
|
||||
/>
|
||||
);
|
||||
render(specDialog);
|
||||
|
|
|
@ -27,7 +27,7 @@ describe('supervisor table action dialog', () => {
|
|||
it('matches snapshot', () => {
|
||||
const supervisorTableActionDialog = (
|
||||
<SupervisorTableActionDialog
|
||||
supervisorId={'test'}
|
||||
supervisorId="test"
|
||||
actions={[basicAction, basicAction, basicAction, basicAction]}
|
||||
onClose={() => {}}
|
||||
/>
|
||||
|
|
|
@ -26,8 +26,8 @@ describe('task table action dialog', () => {
|
|||
it('matches snapshot', () => {
|
||||
const taskTableActionDialog = (
|
||||
<TaskTableActionDialog
|
||||
status={'RUNNING'}
|
||||
taskId={'test'}
|
||||
status="RUNNING"
|
||||
taskId="test"
|
||||
actions={[basicAction]}
|
||||
onClose={() => {}}
|
||||
/>
|
||||
|
|
|
@ -100,9 +100,9 @@ export const COMPACTION_CONFIG_FIELDS: Field<CompactionConfig>[] = [
|
|||
compaction to run, set this field.
|
||||
</p>
|
||||
<p>
|
||||
Directly specify the number of shards to create. If this is specified and 'intervals' is
|
||||
specified in the granularitySpec, the index task can skip the determine
|
||||
intervals/partitions pass through the data.
|
||||
Directly specify the number of shards to create. If this is specified and
|
||||
'intervals' is specified in the granularitySpec, the index task can skip the
|
||||
determine intervals/partitions pass through the data.
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
|
|
|
@ -111,12 +111,12 @@ export const COORDINATOR_DYNAMIC_CONFIG_FIELDS: Field<CoordinatorDynamicConfig>[
|
|||
info: (
|
||||
<>
|
||||
The maximum number of segments that could be queued for loading to any given server. This
|
||||
parameter could be used to speed up segments loading process, especially if there are "slow"
|
||||
nodes in the cluster (with low loading speed) or if too much segments scheduled to be
|
||||
replicated to some particular node (faster loading could be preferred to better segments
|
||||
distribution). Desired value depends on segments loading speed, acceptable replication time
|
||||
and number of nodes. Value 1000 could be a start point for a rather big cluster. Default
|
||||
value is 0 (loading queue is unbounded)
|
||||
parameter could be used to speed up segments loading process, especially if there are
|
||||
"slow" nodes in the cluster (with low loading speed) or if too much segments
|
||||
scheduled to be replicated to some particular node (faster loading could be preferred to
|
||||
better segments distribution). Desired value depends on segments loading speed, acceptable
|
||||
replication time and number of nodes. Value 1000 could be a start point for a rather big
|
||||
cluster. Default value is 0 (loading queue is unbounded)
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
@ -166,9 +166,9 @@ export const COORDINATOR_DYNAMIC_CONFIG_FIELDS: Field<CoordinatorDynamicConfig>[
|
|||
emptyValue: [],
|
||||
info: (
|
||||
<>
|
||||
List of historical services to 'decommission'. Coordinator will not assign new segments to
|
||||
'decommissioning' services, and segments will be moved away from them to be placed on
|
||||
non-decommissioning services at the maximum rate specified by{' '}
|
||||
List of historical services to 'decommission'. Coordinator will not assign new
|
||||
segments to 'decommissioning' services, and segments will be moved away from them
|
||||
to be placed on non-decommissioning services at the maximum rate specified by{' '}
|
||||
<Code>decommissioningMaxPercentOfMaxSegmentsToMove</Code>.
|
||||
</>
|
||||
),
|
||||
|
@ -179,16 +179,16 @@ export const COORDINATOR_DYNAMIC_CONFIG_FIELDS: Field<CoordinatorDynamicConfig>[
|
|||
defaultValue: 70,
|
||||
info: (
|
||||
<>
|
||||
The maximum number of segments that may be moved away from 'decommissioning' services to
|
||||
non-decommissioning (that is, active) services during one Coordinator run. This value is
|
||||
relative to the total maximum segment movements allowed during one run which is determined
|
||||
by <Code>maxSegmentsToMove</Code>. If
|
||||
The maximum number of segments that may be moved away from 'decommissioning'
|
||||
services to non-decommissioning (that is, active) services during one Coordinator run. This
|
||||
value is relative to the total maximum segment movements allowed during one run which is
|
||||
determined by <Code>maxSegmentsToMove</Code>. If
|
||||
<Code>decommissioningMaxPercentOfMaxSegmentsToMove</Code> is 0, segments will neither be
|
||||
moved from or to 'decommissioning' services, effectively putting them in a sort of
|
||||
"maintenance" mode that will not participate in balancing or assignment by load rules.
|
||||
Decommissioning can also become stalled if there are no available active services to place
|
||||
the segments. By leveraging the maximum percent of decommissioning segment movements, an
|
||||
operator can prevent active services from overload by prioritizing balancing, or decrease
|
||||
moved from or to 'decommissioning' services, effectively putting them in a sort of
|
||||
"maintenance" mode that will not participate in balancing or assignment by load
|
||||
rules. Decommissioning can also become stalled if there are no available active services to
|
||||
place the segments. By leveraging the maximum percent of decommissioning segment movements,
|
||||
an operator can prevent active services from overload by prioritizing balancing, or decrease
|
||||
decommissioning time instead. The value should be between 0 and 100.
|
||||
</>
|
||||
),
|
||||
|
@ -205,8 +205,8 @@ export const COORDINATOR_DYNAMIC_CONFIG_FIELDS: Field<CoordinatorDynamicConfig>[
|
|||
server, Druid iterates over the segments on the server, considering them for moving. The
|
||||
default config of 100% means that every segment on every server is a candidate to be moved.
|
||||
This should make sense for most small to medium-sized clusters. However, an admin may find
|
||||
it preferable to drop this value lower if they don't think that it is worthwhile to consider
|
||||
every single segment in the cluster each time it is looking for a segment to move.
|
||||
it preferable to drop this value lower if they don't think that it is worthwhile to
|
||||
consider every single segment in the cluster each time it is looking for a segment to move.
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
|
|
@ -18,16 +18,16 @@
|
|||
|
||||
export * from './compaction-config';
|
||||
export * from './compaction-status';
|
||||
export * from './coordinator-dynamic-config';
|
||||
export * from './dimension-spec';
|
||||
export * from './filter';
|
||||
export * from './flatten-spec';
|
||||
export * from './ingestion-spec';
|
||||
export * from './input-format';
|
||||
export * from './input-source';
|
||||
export * from './lookup-spec';
|
||||
export * from './metric-spec';
|
||||
export * from './overlord-dynamic-config';
|
||||
export * from './time';
|
||||
export * from './timestamp-spec';
|
||||
export * from './transform-spec';
|
||||
export * from './input-source';
|
||||
export * from './input-format';
|
||||
export * from './flatten-spec';
|
||||
export * from './filter';
|
||||
export * from './dimension-spec';
|
||||
export * from './metric-spec';
|
||||
export * from './ingestion-spec';
|
||||
export * from './coordinator-dynamic-config';
|
||||
export * from './overlord-dynamic-config';
|
||||
|
|
|
@ -113,7 +113,7 @@ export function getIngestionComboType(spec: IngestionSpec): IngestionComboType |
|
|||
case 'kinesis':
|
||||
return ioConfig.type;
|
||||
|
||||
case 'index_parallel':
|
||||
case 'index_parallel': {
|
||||
const inputSource = deepGet(spec, 'spec.ioConfig.inputSource') || EMPTY_OBJECT;
|
||||
switch (inputSource.type) {
|
||||
case 'local':
|
||||
|
@ -126,6 +126,7 @@ export function getIngestionComboType(spec: IngestionSpec): IngestionComboType |
|
|||
case 'hdfs':
|
||||
return `${ioConfig.type}:${inputSource.type}` as IngestionComboType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -886,7 +887,7 @@ export function getIoConfigFormFields(ingestionComboType: IngestionComboType): F
|
|||
info: (
|
||||
<>
|
||||
The Amazon Kinesis stream endpoint for a region. You can find a list of endpoints{' '}
|
||||
<ExternalLink href={`https://docs.aws.amazon.com/general/latest/gr/ak.html`}>
|
||||
<ExternalLink href="https://docs.aws.amazon.com/general/latest/gr/ak.html">
|
||||
here
|
||||
</ExternalLink>
|
||||
.
|
||||
|
@ -1054,8 +1055,8 @@ export function getIoConfigTuningFormFields(
|
|||
<p>
|
||||
The maximum number of reading tasks in a replica set. This means that the maximum
|
||||
number of reading tasks will be <Code>taskCount * replicas</Code> and the total
|
||||
number of tasks (reading + publishing) will be higher than this. See 'Capacity
|
||||
Planning' below for more details.
|
||||
number of tasks (reading + publishing) will be higher than this. See 'Capacity
|
||||
Planning' below for more details.
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
|
@ -1212,7 +1213,7 @@ function filterIsFilename(filter: string): boolean {
|
|||
}
|
||||
|
||||
function filenameFromPath(path: string): string | undefined {
|
||||
const m = path.match(/([^\/.]+)[^\/]*?\/?$/);
|
||||
const m = /([^/.]+)[^/]*?\/?$/.exec(path);
|
||||
if (!m) return;
|
||||
return m[1];
|
||||
}
|
||||
|
@ -1233,7 +1234,7 @@ export function guessDataSourceName(spec: IngestionSpec): string | undefined {
|
|||
|
||||
switch (ioConfig.type) {
|
||||
case 'index':
|
||||
case 'index_parallel':
|
||||
case 'index_parallel': {
|
||||
const inputSource = ioConfig.inputSource;
|
||||
if (!inputSource) return;
|
||||
|
||||
|
@ -1249,11 +1250,12 @@ export function guessDataSourceName(spec: IngestionSpec): string | undefined {
|
|||
|
||||
case 's3':
|
||||
case 'azure':
|
||||
case 'google':
|
||||
case 'google': {
|
||||
const actualPath = (inputSource.objects || EMPTY_ARRAY)[0];
|
||||
const uriPath =
|
||||
(inputSource.uris || EMPTY_ARRAY)[0] || (inputSource.prefixes || EMPTY_ARRAY)[0];
|
||||
return actualPath ? actualPath.path : uriPath ? filenameFromPath(uriPath) : undefined;
|
||||
}
|
||||
|
||||
case 'http':
|
||||
return Array.isArray(inputSource.uris)
|
||||
|
@ -1268,6 +1270,7 @@ export function guessDataSourceName(spec: IngestionSpec): string | undefined {
|
|||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case 'kafka':
|
||||
return ioConfig.topic;
|
||||
|
@ -1361,9 +1364,9 @@ export const PRIMARY_PARTITION_RELATED_FORM_FIELDS: Field<IngestionSpec>[] = [
|
|||
info: (
|
||||
<>
|
||||
The granularity to create time chunks at. Multiple segments can be created per time chunk.
|
||||
For example, with 'DAY' segmentGranularity, the events of the same day fall into the same
|
||||
time chunk which can be optionally further partitioned into multiple segments based on other
|
||||
configurations and input size.
|
||||
For example, with 'DAY' segmentGranularity, the events of the same day fall into
|
||||
the same time chunk which can be optionally further partitioned into multiple segments based
|
||||
on other configurations and input size.
|
||||
</>
|
||||
),
|
||||
},
|
||||
|
@ -1485,8 +1488,8 @@ export function getSecondaryPartitionRelatedFormFields(
|
|||
</p>
|
||||
<p>
|
||||
Directly specify the number of shards to create. If this is specified and
|
||||
'intervals' is specified in the granularitySpec, the index task can skip the
|
||||
determine intervals/partitions pass through the data.
|
||||
'intervals' is specified in the granularitySpec, the index task can skip
|
||||
the determine intervals/partitions pass through the data.
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
|
|
|
@ -166,7 +166,8 @@ export const LOOKUP_FIELDS: Field<LookupSpec>[] = [
|
|||
<p>The format of the data in the lookup files.</p>
|
||||
<p>
|
||||
The <Code>simpleJson</Code> lookupParseSpec does not take any parameters. It is simply a
|
||||
line delimited JSON file where the field is the key, and the field's value is the value.
|
||||
line delimited JSON file where the field is the key, and the field's value is the
|
||||
value.
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
|
|
|
@ -315,7 +315,7 @@ export function getMetricSpecSingleFieldName(metricSpec: MetricSpec): string | u
|
|||
|
||||
export function getMetricSpecOutputType(metricSpec: MetricSpec): string | undefined {
|
||||
if (metricSpec.aggregator) return getMetricSpecOutputType(metricSpec.aggregator);
|
||||
const m = String(metricSpec.type).match(/^(long|float|double)/);
|
||||
const m = /^(long|float|double)/.exec(String(metricSpec.type));
|
||||
if (!m) return;
|
||||
return m[1];
|
||||
}
|
||||
|
|
|
@ -52,11 +52,8 @@ const MIN_MICRO = MIN_MILLIS * 1000;
|
|||
const MIN_NANO = MIN_MICRO * 1000;
|
||||
const MAX_NANO = MIN_NANO * 1000;
|
||||
|
||||
// tslint:disable-next-line:max-line-length
|
||||
export const AUTO_MATCHER = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))( ((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)$/;
|
||||
|
||||
// tslint:disable-next-line:max-line-length
|
||||
export const ISO_MATCHER = /^([\+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))(T((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([\.,]\d+(?!:))?)?(\17[0-5]\d([\.,]\d+)?)?([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)$/;
|
||||
export const AUTO_MATCHER = /^([+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))( ((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([.,]\d+(?!:))?)?(\17[0-5]\d([.,]\d+)?)?([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)$/;
|
||||
export const ISO_MATCHER = /^([+-]?\d{4}(?!\d{2}\b))((-?)((0[1-9]|1[0-2])(\3([12]\d|0[1-9]|3[01]))?|W([0-4]\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\d|[12]\d{2}|3([0-5]\d|6[1-6])))(T((([01]\d|2[0-3])((:?)[0-5]\d)?|24:?00)([.,]\d+(?!:))?)?(\17[0-5]\d([.,]\d+)?)?([zZ]|([+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?)$/;
|
||||
|
||||
// Note: AUTO and ISO are basically the same except ISO has a space as a separator instead of the T
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@ import { deepGet, EMPTY_ARRAY, EMPTY_OBJECT } from '../utils';
|
|||
import { IngestionSpec } from './ingestion-spec';
|
||||
import {
|
||||
BASIC_TIME_FORMATS,
|
||||
DATETIME_TIME_FORMATS,
|
||||
DATE_ONLY_TIME_FORMATS,
|
||||
DATETIME_TIME_FORMATS,
|
||||
OTHER_TIME_FORMATS,
|
||||
} from './time';
|
||||
import { Transform } from './transform-spec';
|
||||
|
|
|
@ -17,11 +17,12 @@
|
|||
*/
|
||||
|
||||
import 'core-js/stable';
|
||||
import 'regenerator-runtime/runtime';
|
||||
import './bootstrap/ace';
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import 'regenerator-runtime/runtime';
|
||||
|
||||
import './bootstrap/ace';
|
||||
import { bootstrapReactTable } from './bootstrap/react-table-defaults';
|
||||
import { ConsoleApplication } from './console-application';
|
||||
import { Links, setLinkOverrides } from './links';
|
||||
|
|
|
@ -16,6 +16,6 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './use-interval';
|
||||
export * from './use-global-event-listener';
|
||||
export * from './use-interval';
|
||||
export * from './use-query-manager';
|
||||
|
|
|
@ -56,13 +56,13 @@ export function useQueryManager<Q, R>(
|
|||
return () => {
|
||||
queryManager.terminate();
|
||||
};
|
||||
}, []);
|
||||
}, [initQuery, queryManager]);
|
||||
|
||||
if (query) {
|
||||
useEffect(() => {
|
||||
useEffect(() => {
|
||||
if (query) {
|
||||
queryManager.runQuery(query);
|
||||
}, [query]);
|
||||
}
|
||||
}
|
||||
}, [query, queryManager]);
|
||||
|
||||
return [resultState, queryManager];
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
*/
|
||||
|
||||
import 'core-js/stable';
|
||||
|
||||
import { configure } from 'enzyme';
|
||||
import enzymeAdapterReact16 from 'enzyme-adapter-react-16';
|
||||
|
||||
|
|
|
@ -46,6 +46,6 @@ export class Api {
|
|||
}
|
||||
|
||||
static encodePath(path: string): string {
|
||||
return path.replace(/[?#%&'\[\]\\]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase());
|
||||
return path.replace(/[?#%&'[\]\\]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
export class UrlBaser {
|
||||
static baseUrl: string = '';
|
||||
static baseUrl = '';
|
||||
|
||||
static base(url: string): string {
|
||||
if (!url.startsWith('/')) return url;
|
||||
|
|
|
@ -46,9 +46,9 @@ export class Capabilities {
|
|||
static COORDINATOR: Capabilities;
|
||||
static OVERLORD: Capabilities;
|
||||
|
||||
private queryType: QueryType;
|
||||
private coordinator: boolean;
|
||||
private overlord: boolean;
|
||||
private readonly queryType: QueryType;
|
||||
private readonly coordinator: boolean;
|
||||
private readonly overlord: boolean;
|
||||
|
||||
static async detectQueryType(): Promise<QueryType | undefined> {
|
||||
// Check SQL endpoint
|
||||
|
@ -108,7 +108,7 @@ export class Capabilities {
|
|||
|
||||
static async detectCapabilities(): Promise<Capabilities | undefined> {
|
||||
const capabilitiesOverride = localStorageGetJson(LocalStorageKeys.CAPABILITIES_OVERRIDE);
|
||||
if (capabilitiesOverride) return new Capabilities(capabilitiesOverride as any);
|
||||
if (capabilitiesOverride) return new Capabilities(capabilitiesOverride);
|
||||
|
||||
const queryType = await Capabilities.detectQueryType();
|
||||
if (typeof queryType === 'undefined') return;
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
const UNSAFE_CHAR = /[^a-z0-9 ,._\-;:(){}\[\]<>!@#$%^&*`~?]/gi;
|
||||
const UNSAFE_CHAR = /[^a-z0-9 ,._\-;:(){}[\]<>!@#$%^&*`~?]/gi;
|
||||
|
||||
function escape(str: string): string {
|
||||
return str.replace(UNSAFE_CHAR, s => {
|
||||
|
|
|
@ -62,9 +62,10 @@ export function getDruidErrorMessage(e: any): string {
|
|||
).join(' / ') || e.message
|
||||
);
|
||||
|
||||
case 'string':
|
||||
case 'string': {
|
||||
const htmlResp = parseHtmlError(data);
|
||||
return htmlResp ? `HTML Error: ${htmlResp}` : e.message;
|
||||
}
|
||||
|
||||
default:
|
||||
return e.message;
|
||||
|
@ -73,8 +74,8 @@ export function getDruidErrorMessage(e: any): string {
|
|||
|
||||
export class DruidError extends Error {
|
||||
static parsePosition(errorMessage: string): RowColumn | undefined {
|
||||
const range = String(errorMessage).match(
|
||||
/from line (\d+), column (\d+) to line (\d+), column (\d+)/i,
|
||||
const range = /from line (\d+), column (\d+) to line (\d+), column (\d+)/i.exec(
|
||||
String(errorMessage),
|
||||
);
|
||||
if (range) {
|
||||
return {
|
||||
|
@ -86,7 +87,7 @@ export class DruidError extends Error {
|
|||
};
|
||||
}
|
||||
|
||||
const single = String(errorMessage).match(/at line (\d+), column (\d+)/i);
|
||||
const single = /at line (\d+), column (\d+)/i.exec(String(errorMessage));
|
||||
if (single) {
|
||||
return {
|
||||
match: single[0],
|
||||
|
@ -108,7 +109,7 @@ export class DruidError extends Error {
|
|||
static getSuggestion(errorMessage: string): QuerySuggestion | undefined {
|
||||
// == is used instead of =
|
||||
// ex: Encountered "= =" at line 3, column 15. Was expecting one of
|
||||
const matchEquals = errorMessage.match(/Encountered "= =" at line (\d+), column (\d+)./);
|
||||
const matchEquals = /Encountered "= =" at line (\d+), column (\d+)./.exec(errorMessage);
|
||||
if (matchEquals) {
|
||||
const line = Number(matchEquals[1]);
|
||||
const column = Number(matchEquals[2]);
|
||||
|
@ -124,8 +125,8 @@ export class DruidError extends Error {
|
|||
|
||||
// Incorrect quoting on table
|
||||
// ex: org.apache.calcite.runtime.CalciteContextException: From line 3, column 17 to line 3, column 31: Column '#ar.wikipedia' not found in any table
|
||||
const matchQuotes = errorMessage.match(
|
||||
/org.apache.calcite.runtime.CalciteContextException: From line (\d+), column (\d+) to line \d+, column \d+: Column '([^']+)' not found in any table/,
|
||||
const matchQuotes = /org.apache.calcite.runtime.CalciteContextException: From line (\d+), column (\d+) to line \d+, column \d+: Column '([^']+)' not found in any table/.exec(
|
||||
errorMessage,
|
||||
);
|
||||
if (matchQuotes) {
|
||||
const line = Number(matchQuotes[1]);
|
||||
|
@ -144,7 +145,7 @@ export class DruidError extends Error {
|
|||
}
|
||||
|
||||
// , before FROM
|
||||
const matchComma = errorMessage.match(/Encountered "(FROM)" at/i);
|
||||
const matchComma = /Encountered "(FROM)" at/i.exec(errorMessage);
|
||||
if (matchComma) {
|
||||
const fromKeyword = matchComma[1];
|
||||
return {
|
||||
|
@ -212,7 +213,7 @@ export class DruidError extends Error {
|
|||
}
|
||||
|
||||
export async function queryDruidRune(runeQuery: Record<string, any>): Promise<any> {
|
||||
let runeResultResp: AxiosResponse<any>;
|
||||
let runeResultResp: AxiosResponse;
|
||||
try {
|
||||
runeResultResp = await Api.instance.post('/druid/v2', runeQuery);
|
||||
} catch (e) {
|
||||
|
@ -222,7 +223,7 @@ export async function queryDruidRune(runeQuery: Record<string, any>): Promise<an
|
|||
}
|
||||
|
||||
export async function queryDruidSql<T = any>(sqlQueryPayload: Record<string, any>): Promise<T[]> {
|
||||
let sqlResultResp: AxiosResponse<any>;
|
||||
let sqlResultResp: AxiosResponse;
|
||||
try {
|
||||
sqlResultResp = await Api.instance.post('/druid/v2/sql', sqlQueryPayload);
|
||||
} catch (e) {
|
||||
|
|
|
@ -56,7 +56,7 @@ export function addFilterRaw(filters: Filter[], id: string, value: string): Filt
|
|||
}
|
||||
|
||||
export function makeTextFilter(placeholder = ''): FilterRender {
|
||||
return ({ filter, onChange, key }) => {
|
||||
return function TextFilter({ filter, onChange, key }) {
|
||||
const filterValue = filter ? filter.value : '';
|
||||
return (
|
||||
<InputGroup
|
||||
|
@ -73,7 +73,7 @@ export function makeTextFilter(placeholder = ''): FilterRender {
|
|||
}
|
||||
|
||||
export function makeBooleanFilter(): FilterRender {
|
||||
return ({ filter, onChange, key }) => {
|
||||
return function BooleanFilter({ filter, onChange, key }) {
|
||||
const filterValue = filter ? filter.value : '';
|
||||
return (
|
||||
<HTMLSelect
|
||||
|
@ -318,7 +318,7 @@ export function sortWithPrefixSuffix(
|
|||
// ----------------------------
|
||||
|
||||
export function downloadFile(text: string, type: string, filename: string): void {
|
||||
let blobType: string = '';
|
||||
let blobType;
|
||||
switch (type) {
|
||||
case 'json':
|
||||
blobType = 'application/json';
|
||||
|
|
|
@ -16,13 +16,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './general';
|
||||
export * from './druid-query';
|
||||
export * from './druid-lookup';
|
||||
export * from './query-state';
|
||||
export * from './query-manager';
|
||||
export * from './query-cursor';
|
||||
export * from './local-storage-keys';
|
||||
export * from './column-metadata';
|
||||
export * from './object-change';
|
||||
export * from './capabilities';
|
||||
export * from './column-metadata';
|
||||
export * from './druid-lookup';
|
||||
export * from './druid-query';
|
||||
export * from './general';
|
||||
export * from './local-storage-keys';
|
||||
export * from './object-change';
|
||||
export * from './query-cursor';
|
||||
export * from './query-manager';
|
||||
export * from './query-state';
|
||||
|
|
|
@ -19,26 +19,26 @@
|
|||
import * as JSONBig from 'json-bigint-native';
|
||||
|
||||
export const LocalStorageKeys = {
|
||||
CAPABILITIES_OVERRIDE: 'capabilities-override' as 'capabilities-override',
|
||||
INGESTION_SPEC: 'ingestion-spec' as 'ingestion-spec',
|
||||
DATASOURCE_TABLE_COLUMN_SELECTION: 'datasource-table-column-selection' as 'datasource-table-column-selection',
|
||||
SEGMENT_TABLE_COLUMN_SELECTION: 'segment-table-column-selection' as 'segment-table-column-selection',
|
||||
SUPERVISOR_TABLE_COLUMN_SELECTION: 'supervisor-table-column-selection' as 'supervisor-table-column-selection',
|
||||
TASK_TABLE_COLUMN_SELECTION: 'task-table-column-selection' as 'task-table-column-selection',
|
||||
SERVICE_TABLE_COLUMN_SELECTION: 'service-table-column-selection' as 'service-table-column-selection',
|
||||
LOOKUP_TABLE_COLUMN_SELECTION: 'lookup-table-column-selection' as 'lookup-table-column-selection',
|
||||
QUERY_KEY: 'druid-console-query' as 'druid-console-query',
|
||||
QUERY_CONTEXT: 'query-context' as 'query-context',
|
||||
INGESTION_VIEW_PANE_SIZE: 'ingestion-view-pane-size' as 'ingestion-view-pane-size',
|
||||
QUERY_VIEW_PANE_SIZE: 'query-view-pane-size' as 'query-view-pane-size',
|
||||
TASKS_REFRESH_RATE: 'task-refresh-rate' as 'task-refresh-rate',
|
||||
DATASOURCES_REFRESH_RATE: 'datasources-refresh-rate' as 'datasources-refresh-rate',
|
||||
SEGMENTS_REFRESH_RATE: 'segments-refresh-rate' as 'segments-refresh-rate',
|
||||
SERVICES_REFRESH_RATE: 'services-refresh-rate' as 'services-refresh-rate',
|
||||
SUPERVISORS_REFRESH_RATE: 'supervisors-refresh-rate' as 'supervisors-refresh-rate',
|
||||
LOOKUPS_REFRESH_RATE: 'lookups-refresh-rate' as 'lookups-refresh-rate',
|
||||
QUERY_HISTORY: 'query-history' as 'query-history',
|
||||
LIVE_QUERY_MODE: 'live-query-mode' as 'live-query-mode',
|
||||
CAPABILITIES_OVERRIDE: 'capabilities-override' as const,
|
||||
INGESTION_SPEC: 'ingestion-spec' as const,
|
||||
DATASOURCE_TABLE_COLUMN_SELECTION: 'datasource-table-column-selection' as const,
|
||||
SEGMENT_TABLE_COLUMN_SELECTION: 'segment-table-column-selection' as const,
|
||||
SUPERVISOR_TABLE_COLUMN_SELECTION: 'supervisor-table-column-selection' as const,
|
||||
TASK_TABLE_COLUMN_SELECTION: 'task-table-column-selection' as const,
|
||||
SERVICE_TABLE_COLUMN_SELECTION: 'service-table-column-selection' as const,
|
||||
LOOKUP_TABLE_COLUMN_SELECTION: 'lookup-table-column-selection' as const,
|
||||
QUERY_KEY: 'druid-console-query' as const,
|
||||
QUERY_CONTEXT: 'query-context' as const,
|
||||
INGESTION_VIEW_PANE_SIZE: 'ingestion-view-pane-size' as const,
|
||||
QUERY_VIEW_PANE_SIZE: 'query-view-pane-size' as const,
|
||||
TASKS_REFRESH_RATE: 'task-refresh-rate' as const,
|
||||
DATASOURCES_REFRESH_RATE: 'datasources-refresh-rate' as const,
|
||||
SEGMENTS_REFRESH_RATE: 'segments-refresh-rate' as const,
|
||||
SERVICES_REFRESH_RATE: 'services-refresh-rate' as const,
|
||||
SUPERVISORS_REFRESH_RATE: 'supervisors-refresh-rate' as const,
|
||||
LOOKUPS_REFRESH_RATE: 'lookups-refresh-rate' as const,
|
||||
QUERY_HISTORY: 'query-history' as const,
|
||||
LIVE_QUERY_MODE: 'live-query-mode' as const,
|
||||
};
|
||||
export type LocalStorageKeys = typeof LocalStorageKeys[keyof typeof LocalStorageKeys];
|
||||
|
||||
|
@ -70,5 +70,5 @@ export function localStorageGetJson(key: LocalStorageKeys): any {
|
|||
|
||||
export function localStorageRemove(key: LocalStorageKeys): void {
|
||||
if (typeof localStorage === 'undefined') return;
|
||||
return localStorage.removeItem(key);
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ describe('object-change', () => {
|
|||
const thing = {
|
||||
hello: {
|
||||
'consumer.props': 'lol',
|
||||
wow: ['a', { test: 'moon' }],
|
||||
'wow': ['a', { test: 'moon' }],
|
||||
},
|
||||
zetrix: null,
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*/
|
||||
|
||||
export function shallowCopy(v: any): any {
|
||||
return Array.isArray(v) ? v.slice() : Object.assign({}, v);
|
||||
return Array.isArray(v) ? v.slice() : { ...v };
|
||||
}
|
||||
|
||||
export function isEmpty(v: any): boolean {
|
||||
|
@ -32,14 +32,14 @@ export function parsePath(path: string): string[] {
|
|||
const parts: string[] = [];
|
||||
let rest = path;
|
||||
while (rest) {
|
||||
const escapedMatch = rest.match(/^\{([^{}]*)\}(?:\.(.*))?$/);
|
||||
const escapedMatch = /^\{([^{}]*)\}(?:\.(.*))?$/.exec(rest);
|
||||
if (escapedMatch) {
|
||||
parts.push(escapedMatch[1]);
|
||||
rest = escapedMatch[2];
|
||||
continue;
|
||||
}
|
||||
|
||||
const normalMatch = rest.match(/^([^.]*)(?:\.(.*))?$/);
|
||||
const normalMatch = /^([^.]*)(?:\.(.*))?$/.exec(rest);
|
||||
if (normalMatch) {
|
||||
parts.push(normalMatch[1]);
|
||||
rest = normalMatch[2];
|
||||
|
@ -70,7 +70,7 @@ export function deepGet<T extends Record<string, any>>(value: T, path: string):
|
|||
|
||||
export function deepSet<T extends Record<string, any>>(value: T, path: string, x: any): T {
|
||||
const parts = parsePath(path);
|
||||
let myKey = parts.shift() as string; // Must be defined
|
||||
let myKey = parts.shift()!; // Must be defined
|
||||
const valueCopy = shallowCopy(value);
|
||||
if (Array.isArray(valueCopy) && isAppend(myKey)) myKey = String(valueCopy.length);
|
||||
if (parts.length) {
|
||||
|
@ -102,7 +102,7 @@ export function deepSetMulti<T extends Record<string, any>>(
|
|||
export function deepDelete<T extends Record<string, any>>(value: T, path: string): T {
|
||||
const valueCopy = shallowCopy(value);
|
||||
const parts = parsePath(path);
|
||||
const firstKey = parts.shift() as string; // Must be defined
|
||||
const firstKey = parts.shift()!; // Must be defined
|
||||
if (parts.length) {
|
||||
const firstKeyValue = value[firstKey];
|
||||
if (firstKeyValue) {
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import axios from 'axios';
|
||||
import { CancelToken } from 'axios';
|
||||
import axios, { CancelToken } from 'axios';
|
||||
import debounce from 'lodash.debounce';
|
||||
|
||||
import { QueryState } from './query-state';
|
||||
|
@ -34,12 +33,13 @@ export interface QueryManagerOptions<Q, R> {
|
|||
}
|
||||
|
||||
export class QueryManager<Q, R> {
|
||||
private processQuery: (
|
||||
private readonly processQuery: (
|
||||
query: Q,
|
||||
cancelToken: CancelToken,
|
||||
setIntermediateQuery: (intermediateQuery: any) => void,
|
||||
) => Promise<R>;
|
||||
private onStateChange?: (queryResolve: QueryState<R>) => void;
|
||||
|
||||
private readonly onStateChange?: (queryResolve: QueryState<R>) => void;
|
||||
|
||||
private terminated = false;
|
||||
private nextQuery: Q | undefined;
|
||||
|
@ -49,8 +49,8 @@ export class QueryManager<Q, R> {
|
|||
private state: QueryState<R> = QueryState.INIT;
|
||||
private currentQueryId = 0;
|
||||
|
||||
private runWhenIdle: () => void;
|
||||
private runWhenLoading: () => void;
|
||||
private readonly runWhenIdle: () => void;
|
||||
private readonly runWhenLoading: () => void;
|
||||
|
||||
constructor(options: QueryManagerOptions<Q, R>) {
|
||||
this.processQuery = options.processQuery;
|
||||
|
|
|
@ -31,8 +31,8 @@ import {
|
|||
MetricSpec,
|
||||
PLACEHOLDER_TIMESTAMP_SPEC,
|
||||
REINDEX_TIMESTAMP_SPEC,
|
||||
TimestampSpec,
|
||||
TIME_COLUMN,
|
||||
TimestampSpec,
|
||||
Transform,
|
||||
TransformSpec,
|
||||
upgradeSpec,
|
||||
|
@ -419,7 +419,8 @@ export async function sampleForTimestamp(
|
|||
}
|
||||
|
||||
const sampleTimeData = sampleTime.data;
|
||||
return Object.assign({}, sampleColumns, {
|
||||
return {
|
||||
...sampleColumns,
|
||||
data: sampleColumns.data.map((d, i) => {
|
||||
// Merge the column sample with the time column sample
|
||||
if (!d.parsed) return d;
|
||||
|
@ -427,7 +428,7 @@ export async function sampleForTimestamp(
|
|||
d.parsed.__time = timeDatumParsed ? timeDatumParsed.__time : null;
|
||||
return d;
|
||||
}),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export async function sampleForTransform(
|
||||
|
|
|
@ -69,12 +69,13 @@ describe('utils', () => {
|
|||
it('spec-utils applyCache', () => {
|
||||
expect(
|
||||
applyCache(
|
||||
Object.assign({}, ingestionSpec, {
|
||||
{
|
||||
...ingestionSpec,
|
||||
samplerConfig: {
|
||||
numRows: 500,
|
||||
timeoutMs: 15000,
|
||||
},
|
||||
}),
|
||||
},
|
||||
[
|
||||
{ make: 'Honda', model: 'Accord' },
|
||||
{ make: 'Toyota', model: 'Prius' },
|
||||
|
|
|
@ -24,11 +24,11 @@ import React from 'react';
|
|||
import ReactTable, { Filter } from 'react-table';
|
||||
|
||||
import {
|
||||
ActionCell,
|
||||
ActionIcon,
|
||||
ACTION_COLUMN_ID,
|
||||
ACTION_COLUMN_LABEL,
|
||||
ACTION_COLUMN_WIDTH,
|
||||
ActionCell,
|
||||
ActionIcon,
|
||||
BracedText,
|
||||
MoreButton,
|
||||
RefreshButton,
|
||||
|
@ -71,7 +71,7 @@ import { LocalStorageBackedArray } from '../../utils/local-storage-backed-array'
|
|||
import './datasource-view.scss';
|
||||
|
||||
const tableColumns: Record<CapabilitiesMode, string[]> = {
|
||||
full: [
|
||||
'full': [
|
||||
'Datasource name',
|
||||
'Availability',
|
||||
'Availability detail',
|
||||
|
@ -301,8 +301,12 @@ ORDER BY 1`;
|
|||
}
|
||||
}
|
||||
|
||||
private datasourceQueryManager: QueryManager<DatasourceQuery, DatasourcesAndDefaultRules>;
|
||||
private tiersQueryManager: QueryManager<Capabilities, string[]>;
|
||||
private readonly datasourceQueryManager: QueryManager<
|
||||
DatasourceQuery,
|
||||
DatasourcesAndDefaultRules
|
||||
>;
|
||||
|
||||
private readonly tiersQueryManager: QueryManager<Capabilities, string[]>;
|
||||
|
||||
constructor(props: DatasourcesViewProps, context: any) {
|
||||
super(props, context);
|
||||
|
@ -454,14 +458,14 @@ ORDER BY 1`;
|
|||
});
|
||||
}
|
||||
|
||||
private handleResize = () => {
|
||||
private readonly handleResize = () => {
|
||||
this.setState({
|
||||
chartWidth: window.innerWidth * 0.85,
|
||||
chartHeight: window.innerHeight * 0.4,
|
||||
});
|
||||
};
|
||||
|
||||
private refresh = (auto: any): void => {
|
||||
private readonly refresh = (auto: any): void => {
|
||||
this.datasourceQueryManager.rerunLastQuery(auto);
|
||||
this.tiersQueryManager.rerunLastQuery(auto);
|
||||
};
|
||||
|
@ -695,7 +699,7 @@ ORDER BY 1`;
|
|||
);
|
||||
}
|
||||
|
||||
private saveRules = async (datasource: string, rules: Rule[], comment: string) => {
|
||||
private readonly saveRules = async (datasource: string, rules: Rule[], comment: string) => {
|
||||
try {
|
||||
await Api.instance.post(`/druid/coordinator/v1/rules/${Api.encodePath(datasource)}`, rules, {
|
||||
headers: {
|
||||
|
@ -718,7 +722,7 @@ ORDER BY 1`;
|
|||
this.fetchDatasourceData();
|
||||
};
|
||||
|
||||
private editDefaultRules = () => {
|
||||
private readonly editDefaultRules = () => {
|
||||
this.setState({ retentionDialogOpenOn: undefined });
|
||||
setTimeout(() => {
|
||||
this.setState(state => {
|
||||
|
@ -735,7 +739,7 @@ ORDER BY 1`;
|
|||
}, 50);
|
||||
};
|
||||
|
||||
private saveCompaction = async (compactionConfig: any) => {
|
||||
private readonly saveCompaction = async (compactionConfig: any) => {
|
||||
if (!compactionConfig) return;
|
||||
try {
|
||||
await Api.instance.post(`/druid/coordinator/v1/config/compaction`, compactionConfig);
|
||||
|
@ -749,7 +753,7 @@ ORDER BY 1`;
|
|||
}
|
||||
};
|
||||
|
||||
private deleteCompaction = async () => {
|
||||
private readonly deleteCompaction = () => {
|
||||
const { compactionDialogOpenOn } = this.state;
|
||||
if (!compactionDialogOpenOn) return;
|
||||
const datasource = compactionDialogOpenOn.datasource;
|
||||
|
@ -1417,7 +1421,7 @@ ORDER BY 1`;
|
|||
/>
|
||||
</ViewControlBar>
|
||||
{showChart && (
|
||||
<div className={'chart-container'}>
|
||||
<div className="chart-container">
|
||||
<SegmentTimeline
|
||||
capabilities={capabilities}
|
||||
chartHeight={chartHeight}
|
||||
|
|
|
@ -21,8 +21,7 @@ import React from 'react';
|
|||
|
||||
import { useQueryManager } from '../../../hooks';
|
||||
import { Api } from '../../../singletons';
|
||||
import { pluralIfNeeded, queryDruidSql } from '../../../utils';
|
||||
import { Capabilities } from '../../../utils';
|
||||
import { Capabilities, pluralIfNeeded, queryDruidSql } from '../../../utils';
|
||||
import { HomeViewCard } from '../home-view-card/home-view-card';
|
||||
|
||||
export interface DatasourcesCardProps {
|
||||
|
@ -52,9 +51,9 @@ export const DatasourcesCard = React.memo(function DatasourcesCard(props: Dataso
|
|||
return (
|
||||
<HomeViewCard
|
||||
className="datasources-card"
|
||||
href={'#datasources'}
|
||||
href="#datasources"
|
||||
icon={IconNames.MULTI_SELECT}
|
||||
title={'Datasources'}
|
||||
title="Datasources"
|
||||
loading={datasourceCountState.loading}
|
||||
error={datasourceCountState.error}
|
||||
>
|
||||
|
|
|
@ -27,9 +27,9 @@ describe('home view card', () => {
|
|||
const homeViewCard = (
|
||||
<HomeViewCard
|
||||
className="some-card"
|
||||
href={'#somewhere'}
|
||||
href="#somewhere"
|
||||
icon={IconNames.DATABASE}
|
||||
title={'Something'}
|
||||
title="Something"
|
||||
loading={false}
|
||||
error={undefined}
|
||||
>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue