mirror of https://github.com/apache/druid.git
Web console: fix lookup edit dialog version setting (#10461)
* fix lookup edit dialog * update snapshots * clean up test
This commit is contained in:
parent
d11537b5f7
commit
f77c16bc6c
|
@ -50,6 +50,7 @@ export interface Field<M> {
|
|||
placeholder?: Functor<M, string>;
|
||||
min?: number;
|
||||
zeroMeansUndefined?: boolean;
|
||||
height?: string;
|
||||
disabled?: Functor<M, boolean>;
|
||||
defined?: Functor<M, boolean>;
|
||||
required?: Functor<M, boolean>;
|
||||
|
@ -272,6 +273,7 @@ export class AutoForm<T extends Record<string, any>> extends React.PureComponent
|
|||
value={deepGet(model as any, field.name)}
|
||||
onChange={(v: any) => this.fieldChange(field, v)}
|
||||
placeholder={AutoForm.evaluateFunctor(field.placeholder, model, '')}
|
||||
height={field.height}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`FormJsonSelector matches snapshot form json 1`] = `
|
||||
<Blueprint3.FormGroup
|
||||
className="form-json-selector"
|
||||
>
|
||||
<Blueprint3.ButtonGroup
|
||||
fill={true}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
active={false}
|
||||
onClick={[Function]}
|
||||
text="Form"
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
active={true}
|
||||
onClick={[Function]}
|
||||
text="JSON"
|
||||
/>
|
||||
</Blueprint3.ButtonGroup>
|
||||
</Blueprint3.FormGroup>
|
||||
`;
|
||||
|
||||
exports[`FormJsonSelector matches snapshot form tab 1`] = `
|
||||
<Blueprint3.FormGroup
|
||||
className="form-json-selector"
|
||||
>
|
||||
<Blueprint3.ButtonGroup
|
||||
fill={true}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
active={true}
|
||||
onClick={[Function]}
|
||||
text="Form"
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
active={false}
|
||||
onClick={[Function]}
|
||||
text="JSON"
|
||||
/>
|
||||
</Blueprint3.ButtonGroup>
|
||||
</Blueprint3.FormGroup>
|
||||
`;
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import { FormJsonSelector } from './form-json-selector';
|
||||
|
||||
describe('FormJsonSelector', () => {
|
||||
it('matches snapshot form tab', () => {
|
||||
const formJsonSelector = shallow(<FormJsonSelector tab="form" onChange={() => {}} />);
|
||||
|
||||
expect(formJsonSelector).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('matches snapshot form json', () => {
|
||||
const formJsonSelector = shallow(<FormJsonSelector tab="json" onChange={() => {}} />);
|
||||
|
||||
expect(formJsonSelector).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, ButtonGroup, FormGroup } from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
|
||||
export type FormJsonTabs = 'form' | 'json';
|
||||
|
||||
export interface FormJsonSelectorProps {
|
||||
tab: FormJsonTabs;
|
||||
onChange: (tab: FormJsonTabs) => void;
|
||||
}
|
||||
|
||||
export const FormJsonSelector = React.memo(function FormJsonSelector(props: FormJsonSelectorProps) {
|
||||
const { tab, onChange } = props;
|
||||
|
||||
return (
|
||||
<FormGroup className="form-json-selector">
|
||||
<ButtonGroup fill>
|
||||
<Button text="Form" active={tab === 'form'} onClick={() => onChange('form')} />
|
||||
<Button text="JSON" active={tab === 'json'} onClick={() => onChange('json')} />
|
||||
</ButtonGroup>
|
||||
</FormGroup>
|
||||
);
|
||||
});
|
|
@ -8,24 +8,10 @@ exports[`CompactionDialog matches snapshot with compactionConfig (dynamic partit
|
|||
onClose={[Function]}
|
||||
title="Compaction config: test1"
|
||||
>
|
||||
<Blueprint3.FormGroup
|
||||
className="tabs"
|
||||
>
|
||||
<Blueprint3.ButtonGroup
|
||||
fill={true}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
active={true}
|
||||
onClick={[Function]}
|
||||
text="Form"
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
active={false}
|
||||
onClick={[Function]}
|
||||
text="JSON"
|
||||
/>
|
||||
</Blueprint3.ButtonGroup>
|
||||
</Blueprint3.FormGroup>
|
||||
<Memo(FormJsonSelector)
|
||||
onChange={[Function]}
|
||||
tab="form"
|
||||
/>
|
||||
<div
|
||||
className="content"
|
||||
>
|
||||
|
@ -240,24 +226,10 @@ exports[`CompactionDialog matches snapshot with compactionConfig (hashed partiti
|
|||
onClose={[Function]}
|
||||
title="Compaction config: test1"
|
||||
>
|
||||
<Blueprint3.FormGroup
|
||||
className="tabs"
|
||||
>
|
||||
<Blueprint3.ButtonGroup
|
||||
fill={true}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
active={true}
|
||||
onClick={[Function]}
|
||||
text="Form"
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
active={false}
|
||||
onClick={[Function]}
|
||||
text="JSON"
|
||||
/>
|
||||
</Blueprint3.ButtonGroup>
|
||||
</Blueprint3.FormGroup>
|
||||
<Memo(FormJsonSelector)
|
||||
onChange={[Function]}
|
||||
tab="form"
|
||||
/>
|
||||
<div
|
||||
className="content"
|
||||
>
|
||||
|
@ -472,24 +444,10 @@ exports[`CompactionDialog matches snapshot with compactionConfig (single_dim par
|
|||
onClose={[Function]}
|
||||
title="Compaction config: test1"
|
||||
>
|
||||
<Blueprint3.FormGroup
|
||||
className="tabs"
|
||||
>
|
||||
<Blueprint3.ButtonGroup
|
||||
fill={true}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
active={true}
|
||||
onClick={[Function]}
|
||||
text="Form"
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
active={false}
|
||||
onClick={[Function]}
|
||||
text="JSON"
|
||||
/>
|
||||
</Blueprint3.ButtonGroup>
|
||||
</Blueprint3.FormGroup>
|
||||
<Memo(FormJsonSelector)
|
||||
onChange={[Function]}
|
||||
tab="form"
|
||||
/>
|
||||
<div
|
||||
className="content"
|
||||
>
|
||||
|
@ -704,24 +662,10 @@ exports[`CompactionDialog matches snapshot without compactionConfig 1`] = `
|
|||
onClose={[Function]}
|
||||
title="Compaction config: test1"
|
||||
>
|
||||
<Blueprint3.FormGroup
|
||||
className="tabs"
|
||||
>
|
||||
<Blueprint3.ButtonGroup
|
||||
fill={true}
|
||||
>
|
||||
<Blueprint3.Button
|
||||
active={true}
|
||||
onClick={[Function]}
|
||||
text="Form"
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
active={false}
|
||||
onClick={[Function]}
|
||||
text="JSON"
|
||||
/>
|
||||
</Blueprint3.ButtonGroup>
|
||||
</Blueprint3.FormGroup>
|
||||
<Memo(FormJsonSelector)
|
||||
onChange={[Function]}
|
||||
tab="form"
|
||||
/>
|
||||
<div
|
||||
className="content"
|
||||
>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
height: 80vh;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
.form-json-selector {
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,16 +16,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, ButtonGroup, Classes, Code, Dialog, FormGroup, Intent } from '@blueprintjs/core';
|
||||
import { Button, Classes, Code, Dialog, Intent } from '@blueprintjs/core';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { AutoForm, Field, JsonInput } from '../../components';
|
||||
import {
|
||||
FormJsonSelector,
|
||||
FormJsonTabs,
|
||||
} from '../../components/form-json-selector/form-json-selector';
|
||||
import { deepGet, deepSet } from '../../utils/object-change';
|
||||
|
||||
import './compaction-dialog.scss';
|
||||
|
||||
type Tabs = 'form' | 'json';
|
||||
|
||||
type CompactionConfig = Record<string, any>;
|
||||
|
||||
const COMPACTION_CONFIG_FIELDS: Field<CompactionConfig>[] = [
|
||||
|
@ -240,7 +242,7 @@ export interface CompactionDialogProps {
|
|||
export const CompactionDialog = React.memo(function CompactionDialog(props: CompactionDialogProps) {
|
||||
const { datasource, compactionConfig, onSave, onClose, onDelete } = props;
|
||||
|
||||
const [currentTab, setCurrentTab] = useState<Tabs>('form');
|
||||
const [currentTab, setCurrentTab] = useState<FormJsonTabs>('form');
|
||||
const [currentConfig, setCurrentConfig] = useState<CompactionConfig>(
|
||||
compactionConfig || {
|
||||
dataSource: datasource,
|
||||
|
@ -261,20 +263,7 @@ export const CompactionDialog = React.memo(function CompactionDialog(props: Comp
|
|||
canOutsideClickClose={false}
|
||||
title={`Compaction config: ${datasource}`}
|
||||
>
|
||||
<FormGroup className="tabs">
|
||||
<ButtonGroup fill>
|
||||
<Button
|
||||
text="Form"
|
||||
active={currentTab === 'form'}
|
||||
onClick={() => setCurrentTab('form')}
|
||||
/>
|
||||
<Button
|
||||
text="JSON"
|
||||
active={currentTab === 'json'}
|
||||
onClick={() => setCurrentTab('json')}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</FormGroup>
|
||||
<FormJsonSelector tab={currentTab} onChange={setCurrentTab} />
|
||||
<div className="content">
|
||||
{currentTab === 'form' ? (
|
||||
<AutoForm
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`lookup edit dialog matches snapshot 1`] = `
|
||||
exports[`LookupEditDialog matches snapshot 1`] = `
|
||||
<Blueprint3.Dialog
|
||||
canOutsideClickClose={true}
|
||||
className="lookup-edit-dialog"
|
||||
|
@ -8,405 +8,364 @@ exports[`lookup edit dialog matches snapshot 1`] = `
|
|||
onClose={[Function]}
|
||||
title="Add lookup"
|
||||
>
|
||||
<Blueprint3.FormGroup
|
||||
className="lookup-label"
|
||||
label="Name"
|
||||
<div
|
||||
className="content"
|
||||
>
|
||||
<Blueprint3.InputGroup
|
||||
disabled={false}
|
||||
onChange={[Function]}
|
||||
placeholder="Enter the lookup name"
|
||||
value="test"
|
||||
/>
|
||||
</Blueprint3.FormGroup>
|
||||
<Blueprint3.FormGroup
|
||||
className="lookup-label"
|
||||
label="Tier"
|
||||
>
|
||||
<HTMLSelect
|
||||
disabled={false}
|
||||
onChange={[Function]}
|
||||
value="test"
|
||||
<Blueprint3.FormGroup
|
||||
label="Name"
|
||||
>
|
||||
<option
|
||||
key="a"
|
||||
value="a"
|
||||
<Blueprint3.InputGroup
|
||||
disabled={false}
|
||||
onChange={[Function]}
|
||||
placeholder="Enter the lookup name"
|
||||
value="test"
|
||||
/>
|
||||
</Blueprint3.FormGroup>
|
||||
<Blueprint3.FormGroup
|
||||
label="Tier"
|
||||
>
|
||||
<HTMLSelect
|
||||
onChange={[Function]}
|
||||
value="test"
|
||||
>
|
||||
a
|
||||
</option>
|
||||
<option
|
||||
key="b"
|
||||
value="b"
|
||||
>
|
||||
b
|
||||
</option>
|
||||
<option
|
||||
key="c"
|
||||
value="c"
|
||||
>
|
||||
c
|
||||
</option>
|
||||
<option
|
||||
key="d"
|
||||
value="d"
|
||||
>
|
||||
d
|
||||
</option>
|
||||
<option
|
||||
key="e"
|
||||
value="e"
|
||||
>
|
||||
e
|
||||
</option>
|
||||
<option
|
||||
key="f"
|
||||
value="f"
|
||||
>
|
||||
f
|
||||
</option>
|
||||
<option
|
||||
key="g"
|
||||
value="g"
|
||||
>
|
||||
g
|
||||
</option>
|
||||
<option
|
||||
key="h"
|
||||
value="h"
|
||||
>
|
||||
h
|
||||
</option>
|
||||
<option
|
||||
key="i"
|
||||
value="i"
|
||||
>
|
||||
i
|
||||
</option>
|
||||
<option
|
||||
key="j"
|
||||
value="j"
|
||||
>
|
||||
j
|
||||
</option>
|
||||
</HTMLSelect>
|
||||
</Blueprint3.FormGroup>
|
||||
<Blueprint3.FormGroup
|
||||
className="lookup-label"
|
||||
label="Version"
|
||||
>
|
||||
<Blueprint3.InputGroup
|
||||
<option
|
||||
key="__default"
|
||||
value="__default"
|
||||
>
|
||||
__default
|
||||
</option>
|
||||
<option
|
||||
key="alt-tier"
|
||||
value="alt-tier"
|
||||
>
|
||||
alt-tier
|
||||
</option>
|
||||
</HTMLSelect>
|
||||
</Blueprint3.FormGroup>
|
||||
<Blueprint3.FormGroup
|
||||
label="Version"
|
||||
>
|
||||
<Blueprint3.InputGroup
|
||||
onChange={[Function]}
|
||||
placeholder="Enter the lookup version"
|
||||
rightElement={
|
||||
<Blueprint3.Button
|
||||
minimal={true}
|
||||
onClick={[Function]}
|
||||
text="Use ISO as version"
|
||||
/>
|
||||
}
|
||||
value="test"
|
||||
/>
|
||||
</Blueprint3.FormGroup>
|
||||
<Memo(FormJsonSelector)
|
||||
onChange={[Function]}
|
||||
placeholder="Enter the lookup version"
|
||||
rightElement={
|
||||
<Blueprint3.Button
|
||||
minimal={true}
|
||||
onClick={[Function]}
|
||||
text="Use ISO as version"
|
||||
/>
|
||||
}
|
||||
value="test"
|
||||
tab="form"
|
||||
/>
|
||||
</Blueprint3.FormGroup>
|
||||
<AutoForm
|
||||
fields={
|
||||
Array [
|
||||
Object {
|
||||
"adjustment": [Function],
|
||||
"name": "type",
|
||||
"suggestions": Array [
|
||||
"map",
|
||||
"cachedNamespace",
|
||||
],
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"name": "map",
|
||||
"type": "json",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"label": "Globally cached lookup type",
|
||||
"name": "extractionNamespace.type",
|
||||
"placeholder": "uri",
|
||||
"suggestions": Array [
|
||||
"uri",
|
||||
"jdbc",
|
||||
],
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "A URI which specifies a directory (or other searchable resource) in which to search for files",
|
||||
"label": "URI prefix",
|
||||
"name": "extractionNamespace.uriPrefix",
|
||||
"placeholder": "s3://bucket/some/key/prefix/",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Optional regex for matching the file name under uriPrefix. Only used if uriPrefix is used",
|
||||
"label": "File regex",
|
||||
"name": "extractionNamespace.fileRegex",
|
||||
"placeholder": "(optional)",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defaultValue": "csv",
|
||||
"defined": [Function],
|
||||
"label": "Format",
|
||||
"name": "extractionNamespace.namespaceParseSpec.format",
|
||||
"suggestions": Array [
|
||||
"csv",
|
||||
"tsv",
|
||||
"customJson",
|
||||
"simpleJson",
|
||||
],
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "The list of columns in the csv file",
|
||||
"label": "Columns",
|
||||
"name": "extractionNamespace.namespaceParseSpec.columns",
|
||||
"placeholder": "[\\"key\\", \\"value\\"]",
|
||||
"type": "string-array",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "The name of the column containing the key",
|
||||
"label": "Key column",
|
||||
"name": "extractionNamespace.namespaceParseSpec.keyColumn",
|
||||
"placeholder": "Key",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "The name of the column containing the value",
|
||||
"label": "Value column",
|
||||
"name": "extractionNamespace.namespaceParseSpec.valueColumn",
|
||||
"placeholder": "Value",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defaultValue": false,
|
||||
"defined": [Function],
|
||||
"info": "A flag to indicate that column information can be extracted from the input files' header row",
|
||||
"label": "Has header row",
|
||||
"name": "extractionNamespace.namespaceParseSpec.hasHeaderRow",
|
||||
"type": "boolean",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Number of header rows to be skipped. The default number of header rows to be skipped is 0.",
|
||||
"label": "Skip header rows",
|
||||
"name": "extractionNamespace.namespaceParseSpec.skipHeaderRows",
|
||||
"placeholder": "(optional)",
|
||||
"type": "number",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"label": "Delimiter",
|
||||
"name": "extractionNamespace.namespaceParseSpec.delimiter",
|
||||
"placeholder": "(optional)",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"label": "List delimiter",
|
||||
"name": "extractionNamespace.namespaceParseSpec.listDelimiter",
|
||||
"placeholder": "(optional)",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"label": "Key field name",
|
||||
"name": "extractionNamespace.namespaceParseSpec.keyFieldName",
|
||||
"placeholder": "key",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"label": "Value field name",
|
||||
"name": "extractionNamespace.namespaceParseSpec.valueFieldName",
|
||||
"placeholder": "value",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": <React.Fragment>
|
||||
<p>
|
||||
The namespace value in the SQL query:
|
||||
</p>
|
||||
<p>
|
||||
SELECT keyColumn, valueColumn, tsColumn? FROM
|
||||
<strong>
|
||||
namespace
|
||||
</strong>
|
||||
.table WHERE filter
|
||||
</p>
|
||||
</React.Fragment>,
|
||||
"label": "Namespace",
|
||||
"name": "extractionNamespace.namespace",
|
||||
"placeholder": "some_lookup",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Defines the connectURI value on the The connector config to used",
|
||||
"label": "CreateTables",
|
||||
"name": "extractionNamespace.connectorConfig.createTables",
|
||||
"type": "boolean",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Defines the connectURI value on the The connector config to used",
|
||||
"label": "Connect URI",
|
||||
"name": "extractionNamespace.connectorConfig.connectURI",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Defines the user to be used by the connector config",
|
||||
"label": "User",
|
||||
"name": "extractionNamespace.connectorConfig.user",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Defines the password to be used by the connector config",
|
||||
"label": "Password",
|
||||
"name": "extractionNamespace.connectorConfig.password",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": <React.Fragment>
|
||||
<p>
|
||||
The table which contains the key value pairs. This will become the table value in the SQL query:
|
||||
</p>
|
||||
<p>
|
||||
SELECT keyColumn, valueColumn, tsColumn? FROM namespace.
|
||||
<strong>
|
||||
table
|
||||
</strong>
|
||||
WHERE filter
|
||||
</p>
|
||||
</React.Fragment>,
|
||||
"label": "Table",
|
||||
"name": "extractionNamespace.table",
|
||||
"placeholder": "some_lookup_table",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": <React.Fragment>
|
||||
<p>
|
||||
The column in the table which contains the keys. This will become the keyColumn value in the SQL query:
|
||||
</p>
|
||||
<p>
|
||||
SELECT
|
||||
<strong>
|
||||
keyColumn
|
||||
</strong>
|
||||
, valueColumn, tsColumn? FROM namespace.table WHERE filter
|
||||
</p>
|
||||
</React.Fragment>,
|
||||
"label": "Key column",
|
||||
"name": "extractionNamespace.keyColumn",
|
||||
"placeholder": "my_key_value",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": <React.Fragment>
|
||||
<p>
|
||||
The column in table which contains the values. This will become the valueColumn value in the SQL query:
|
||||
</p>
|
||||
<p>
|
||||
SELECT keyColumn,
|
||||
<strong>
|
||||
valueColumn
|
||||
</strong>
|
||||
, tsColumn? FROM namespace.table WHERE filter
|
||||
</p>
|
||||
</React.Fragment>,
|
||||
"label": "Value column",
|
||||
"name": "extractionNamespace.valueColumn",
|
||||
"placeholder": "my_column_value",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": <React.Fragment>
|
||||
<p>
|
||||
The filter to be used when selecting lookups, this is used to create a where clause on lookup population. This will become the expression filter in the SQL query:
|
||||
</p>
|
||||
<p>
|
||||
SELECT keyColumn, valueColumn, tsColumn? FROM namespace.table WHERE
|
||||
|
||||
<strong>
|
||||
filter
|
||||
</strong>
|
||||
</p>
|
||||
</React.Fragment>,
|
||||
"label": "Filter",
|
||||
"name": "extractionNamespace.filter",
|
||||
"placeholder": "(optional)",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": <React.Fragment>
|
||||
<p>
|
||||
The column in table which contains when the key was updated. This will become the Value in the SQL query:
|
||||
</p>
|
||||
<p>
|
||||
SELECT keyColumn, valueColumn,
|
||||
<strong>
|
||||
tsColumn
|
||||
</strong>
|
||||
? FROM namespace.table WHERE filter
|
||||
</p>
|
||||
</React.Fragment>,
|
||||
"label": "TsColumn",
|
||||
"name": "extractionNamespace.tsColumn",
|
||||
"placeholder": "(optional)",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Period between polling for updates",
|
||||
"label": "Poll period",
|
||||
"name": "extractionNamespace.pollPeriod",
|
||||
"placeholder": "(optional)",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "How long to wait (in ms) for the first run of the cache to populate. 0 indicates to not wait",
|
||||
"label": "First cache timeout",
|
||||
"name": "firstCacheTimeout",
|
||||
"placeholder": "(optional)",
|
||||
"type": "number",
|
||||
},
|
||||
Object {
|
||||
"defaultValue": false,
|
||||
"defined": [Function],
|
||||
"info": "If the underlying map is injective (keys and values are unique) then optimizations can occur internally by setting this to true",
|
||||
"name": "injective",
|
||||
"type": "boolean",
|
||||
},
|
||||
]
|
||||
}
|
||||
model={
|
||||
Object {
|
||||
"map": Object {},
|
||||
"type": "map",
|
||||
<AutoForm
|
||||
fields={
|
||||
Array [
|
||||
Object {
|
||||
"adjustment": [Function],
|
||||
"name": "type",
|
||||
"suggestions": Array [
|
||||
"map",
|
||||
"cachedNamespace",
|
||||
],
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"height": "60vh",
|
||||
"name": "map",
|
||||
"type": "json",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"label": "Globally cached lookup type",
|
||||
"name": "extractionNamespace.type",
|
||||
"placeholder": "uri",
|
||||
"suggestions": Array [
|
||||
"uri",
|
||||
"jdbc",
|
||||
],
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "A URI which specifies a directory (or other searchable resource) in which to search for files",
|
||||
"label": "URI prefix",
|
||||
"name": "extractionNamespace.uriPrefix",
|
||||
"placeholder": "s3://bucket/some/key/prefix/",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Optional regex for matching the file name under uriPrefix. Only used if uriPrefix is used",
|
||||
"label": "File regex",
|
||||
"name": "extractionNamespace.fileRegex",
|
||||
"placeholder": "(optional)",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defaultValue": "csv",
|
||||
"defined": [Function],
|
||||
"label": "Format",
|
||||
"name": "extractionNamespace.namespaceParseSpec.format",
|
||||
"suggestions": Array [
|
||||
"csv",
|
||||
"tsv",
|
||||
"customJson",
|
||||
"simpleJson",
|
||||
],
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "The list of columns in the csv file",
|
||||
"label": "Columns",
|
||||
"name": "extractionNamespace.namespaceParseSpec.columns",
|
||||
"placeholder": "[\\"key\\", \\"value\\"]",
|
||||
"type": "string-array",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "The name of the column containing the key",
|
||||
"label": "Key column",
|
||||
"name": "extractionNamespace.namespaceParseSpec.keyColumn",
|
||||
"placeholder": "Key",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "The name of the column containing the value",
|
||||
"label": "Value column",
|
||||
"name": "extractionNamespace.namespaceParseSpec.valueColumn",
|
||||
"placeholder": "Value",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defaultValue": false,
|
||||
"defined": [Function],
|
||||
"info": "A flag to indicate that column information can be extracted from the input files' header row",
|
||||
"label": "Has header row",
|
||||
"name": "extractionNamespace.namespaceParseSpec.hasHeaderRow",
|
||||
"type": "boolean",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Number of header rows to be skipped. The default number of header rows to be skipped is 0.",
|
||||
"label": "Skip header rows",
|
||||
"name": "extractionNamespace.namespaceParseSpec.skipHeaderRows",
|
||||
"placeholder": "(optional)",
|
||||
"type": "number",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"label": "Delimiter",
|
||||
"name": "extractionNamespace.namespaceParseSpec.delimiter",
|
||||
"placeholder": "(optional)",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"label": "List delimiter",
|
||||
"name": "extractionNamespace.namespaceParseSpec.listDelimiter",
|
||||
"placeholder": "(optional)",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"label": "Key field name",
|
||||
"name": "extractionNamespace.namespaceParseSpec.keyFieldName",
|
||||
"placeholder": "key",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"label": "Value field name",
|
||||
"name": "extractionNamespace.namespaceParseSpec.valueFieldName",
|
||||
"placeholder": "value",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": <React.Fragment>
|
||||
<p>
|
||||
The namespace value in the SQL query:
|
||||
</p>
|
||||
<p>
|
||||
SELECT keyColumn, valueColumn, tsColumn? FROM
|
||||
<strong>
|
||||
namespace
|
||||
</strong>
|
||||
.table WHERE filter
|
||||
</p>
|
||||
</React.Fragment>,
|
||||
"label": "Namespace",
|
||||
"name": "extractionNamespace.namespace",
|
||||
"placeholder": "some_lookup",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Defines the connectURI value on the The connector config to used",
|
||||
"label": "CreateTables",
|
||||
"name": "extractionNamespace.connectorConfig.createTables",
|
||||
"type": "boolean",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Defines the connectURI value on the The connector config to used",
|
||||
"label": "Connect URI",
|
||||
"name": "extractionNamespace.connectorConfig.connectURI",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Defines the user to be used by the connector config",
|
||||
"label": "User",
|
||||
"name": "extractionNamespace.connectorConfig.user",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Defines the password to be used by the connector config",
|
||||
"label": "Password",
|
||||
"name": "extractionNamespace.connectorConfig.password",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": <React.Fragment>
|
||||
<p>
|
||||
The table which contains the key value pairs. This will become the table value in the SQL query:
|
||||
</p>
|
||||
<p>
|
||||
SELECT keyColumn, valueColumn, tsColumn? FROM namespace.
|
||||
<strong>
|
||||
table
|
||||
</strong>
|
||||
WHERE filter
|
||||
</p>
|
||||
</React.Fragment>,
|
||||
"label": "Table",
|
||||
"name": "extractionNamespace.table",
|
||||
"placeholder": "some_lookup_table",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": <React.Fragment>
|
||||
<p>
|
||||
The column in the table which contains the keys. This will become the keyColumn value in the SQL query:
|
||||
</p>
|
||||
<p>
|
||||
SELECT
|
||||
<strong>
|
||||
keyColumn
|
||||
</strong>
|
||||
, valueColumn, tsColumn? FROM namespace.table WHERE filter
|
||||
</p>
|
||||
</React.Fragment>,
|
||||
"label": "Key column",
|
||||
"name": "extractionNamespace.keyColumn",
|
||||
"placeholder": "my_key_value",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": <React.Fragment>
|
||||
<p>
|
||||
The column in table which contains the values. This will become the valueColumn value in the SQL query:
|
||||
</p>
|
||||
<p>
|
||||
SELECT keyColumn,
|
||||
<strong>
|
||||
valueColumn
|
||||
</strong>
|
||||
, tsColumn? FROM namespace.table WHERE filter
|
||||
</p>
|
||||
</React.Fragment>,
|
||||
"label": "Value column",
|
||||
"name": "extractionNamespace.valueColumn",
|
||||
"placeholder": "my_column_value",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": <React.Fragment>
|
||||
<p>
|
||||
The filter to be used when selecting lookups, this is used to create a where clause on lookup population. This will become the expression filter in the SQL query:
|
||||
</p>
|
||||
<p>
|
||||
SELECT keyColumn, valueColumn, tsColumn? FROM namespace.table WHERE
|
||||
|
||||
<strong>
|
||||
filter
|
||||
</strong>
|
||||
</p>
|
||||
</React.Fragment>,
|
||||
"label": "Filter",
|
||||
"name": "extractionNamespace.filter",
|
||||
"placeholder": "(optional)",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": <React.Fragment>
|
||||
<p>
|
||||
The column in table which contains when the key was updated. This will become the Value in the SQL query:
|
||||
</p>
|
||||
<p>
|
||||
SELECT keyColumn, valueColumn,
|
||||
<strong>
|
||||
tsColumn
|
||||
</strong>
|
||||
? FROM namespace.table WHERE filter
|
||||
</p>
|
||||
</React.Fragment>,
|
||||
"label": "TsColumn",
|
||||
"name": "extractionNamespace.tsColumn",
|
||||
"placeholder": "(optional)",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "Period between polling for updates",
|
||||
"label": "Poll period",
|
||||
"name": "extractionNamespace.pollPeriod",
|
||||
"placeholder": "(optional)",
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"defined": [Function],
|
||||
"info": "How long to wait (in ms) for the first run of the cache to populate. 0 indicates to not wait",
|
||||
"label": "First cache timeout",
|
||||
"name": "firstCacheTimeout",
|
||||
"placeholder": "(optional)",
|
||||
"type": "number",
|
||||
},
|
||||
Object {
|
||||
"defaultValue": false,
|
||||
"defined": [Function],
|
||||
"info": "If the underlying map is injective (keys and values are unique) then optimizations can occur internally by setting this to true",
|
||||
"name": "injective",
|
||||
"type": "boolean",
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
onChange={[Function]}
|
||||
/>
|
||||
model={
|
||||
Object {
|
||||
"map": Object {
|
||||
"a": 1,
|
||||
},
|
||||
"type": "map",
|
||||
}
|
||||
}
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="bp3-dialog-footer"
|
||||
>
|
||||
|
|
|
@ -18,19 +18,15 @@
|
|||
|
||||
.lookup-edit-dialog {
|
||||
&.bp3-dialog {
|
||||
top: 10vh;
|
||||
|
||||
height: 80vh;
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.auto-form {
|
||||
margin: 5px 20px 10px;
|
||||
}
|
||||
|
||||
.lookup-label {
|
||||
padding: 0 20px;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
.content {
|
||||
margin: 0 15px 10px 0;
|
||||
padding: 15px 5px 5px 15px;
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.ace-solarized-dark {
|
||||
|
|
|
@ -21,7 +21,7 @@ import React from 'react';
|
|||
|
||||
import { isLookupSubmitDisabled, LookupEditDialog } from './lookup-edit-dialog';
|
||||
|
||||
describe('lookup edit dialog', () => {
|
||||
describe('LookupEditDialog', () => {
|
||||
it('matches snapshot', () => {
|
||||
const lookupEditDialog = shallow(
|
||||
<LookupEditDialog
|
||||
|
@ -31,9 +31,9 @@ describe('lookup edit dialog', () => {
|
|||
lookupName={'test'}
|
||||
lookupTier={'test'}
|
||||
lookupVersion={'test'}
|
||||
lookupSpec={{ type: 'map', map: {} }}
|
||||
lookupSpec={{ type: 'map', map: { a: 1 } }}
|
||||
isEdit={false}
|
||||
allLookupTiers={['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']}
|
||||
allLookupTiers={['__default', 'alt-tier']}
|
||||
/>,
|
||||
);
|
||||
|
||||
|
|
|
@ -25,9 +25,13 @@ import {
|
|||
InputGroup,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { AutoForm, Field } from '../../components';
|
||||
import { AutoForm, Field, JsonInput } from '../../components';
|
||||
import {
|
||||
FormJsonSelector,
|
||||
FormJsonTabs,
|
||||
} from '../../components/form-json-selector/form-json-selector';
|
||||
|
||||
import './lookup-edit-dialog.scss';
|
||||
|
||||
|
@ -158,9 +162,8 @@ const LOOKUP_FIELDS: Field<LookupSpec>[] = [
|
|||
{
|
||||
name: 'map',
|
||||
type: 'json',
|
||||
defined: (model: LookupSpec) => {
|
||||
return model.type === 'map';
|
||||
},
|
||||
height: '60vh',
|
||||
defined: (model: LookupSpec) => model.type === 'map',
|
||||
},
|
||||
{
|
||||
name: 'extractionNamespace.type',
|
||||
|
@ -552,47 +555,8 @@ export const LookupEditDialog = React.memo(function LookupEditDialog(props: Look
|
|||
isEdit,
|
||||
allLookupTiers,
|
||||
} = props;
|
||||
|
||||
let updateVersionOnSubmit = true;
|
||||
|
||||
function addISOVersion() {
|
||||
const currentDate = new Date();
|
||||
const ISOString = currentDate.toISOString();
|
||||
onChange('version', ISOString);
|
||||
}
|
||||
|
||||
function renderTierInput() {
|
||||
if (isEdit) {
|
||||
return (
|
||||
<FormGroup className="lookup-label" label="Tier">
|
||||
<InputGroup
|
||||
value={lookupTier}
|
||||
onChange={(e: any) => {
|
||||
updateVersionOnSubmit = false;
|
||||
onChange('tier', e.target.value);
|
||||
}}
|
||||
disabled
|
||||
/>
|
||||
</FormGroup>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<FormGroup className="lookup-label" label="Tier">
|
||||
<HTMLSelect
|
||||
disabled={isEdit}
|
||||
value={lookupTier}
|
||||
onChange={(e: any) => onChange('tier', e.target.value)}
|
||||
>
|
||||
{allLookupTiers.map(tier => (
|
||||
<option key={tier} value={tier}>
|
||||
{tier}
|
||||
</option>
|
||||
))}
|
||||
</HTMLSelect>
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
const [currentTab, setCurrentTab] = useState<FormJsonTabs>('form');
|
||||
const [updateVersionOnSubmit, setUpdateVersionOnSubmit] = useState(true);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
@ -601,32 +565,68 @@ export const LookupEditDialog = React.memo(function LookupEditDialog(props: Look
|
|||
onClose={onClose}
|
||||
title={isEdit ? 'Edit lookup' : 'Add lookup'}
|
||||
>
|
||||
<FormGroup className="lookup-label" label="Name">
|
||||
<InputGroup
|
||||
value={lookupName}
|
||||
onChange={(e: any) => onChange('name', e.target.value)}
|
||||
disabled={isEdit}
|
||||
placeholder="Enter the lookup name"
|
||||
/>
|
||||
</FormGroup>
|
||||
{renderTierInput()}
|
||||
<FormGroup className="lookup-label" label="Version">
|
||||
<InputGroup
|
||||
value={lookupVersion}
|
||||
onChange={(e: any) => onChange('version', e.target.value)}
|
||||
placeholder="Enter the lookup version"
|
||||
rightElement={
|
||||
<Button minimal text="Use ISO as version" onClick={() => addISOVersion()} />
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
<AutoForm
|
||||
fields={LOOKUP_FIELDS}
|
||||
model={lookupSpec}
|
||||
onChange={m => {
|
||||
onChange('spec', m);
|
||||
}}
|
||||
/>
|
||||
<div className="content">
|
||||
<FormGroup label="Name">
|
||||
<InputGroup
|
||||
value={lookupName}
|
||||
onChange={(e: any) => onChange('name', e.target.value)}
|
||||
disabled={isEdit}
|
||||
placeholder="Enter the lookup name"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup label="Tier">
|
||||
{isEdit ? (
|
||||
<InputGroup
|
||||
value={lookupTier}
|
||||
onChange={(e: any) => onChange('tier', e.target.value)}
|
||||
disabled
|
||||
/>
|
||||
) : (
|
||||
<HTMLSelect value={lookupTier} onChange={(e: any) => onChange('tier', e.target.value)}>
|
||||
{allLookupTiers.map(tier => (
|
||||
<option key={tier} value={tier}>
|
||||
{tier}
|
||||
</option>
|
||||
))}
|
||||
</HTMLSelect>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup label="Version">
|
||||
<InputGroup
|
||||
value={lookupVersion}
|
||||
onChange={(e: any) => {
|
||||
setUpdateVersionOnSubmit(false);
|
||||
onChange('version', e.target.value);
|
||||
}}
|
||||
placeholder="Enter the lookup version"
|
||||
rightElement={
|
||||
<Button
|
||||
minimal
|
||||
text="Use ISO as version"
|
||||
onClick={() => onChange('version', new Date().toISOString())}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormJsonSelector tab={currentTab} onChange={setCurrentTab} />
|
||||
{currentTab === 'form' ? (
|
||||
<AutoForm
|
||||
fields={LOOKUP_FIELDS}
|
||||
model={lookupSpec}
|
||||
onChange={m => {
|
||||
onChange('spec', m);
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<JsonInput
|
||||
value={lookupSpec}
|
||||
onChange={m => {
|
||||
onChange('spec', m);
|
||||
}}
|
||||
height="80vh"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button text="Close" onClick={onClose} />
|
||||
|
|
|
@ -893,9 +893,10 @@ GROUP BY 1`;
|
|||
data={datasources}
|
||||
loading={datasourcesAndDefaultRulesState.loading}
|
||||
noDataText={
|
||||
!datasourcesAndDefaultRulesState.loading && datasources && !datasources.length
|
||||
datasourcesAndDefaultRulesState.getErrorMessage() ||
|
||||
(!datasourcesAndDefaultRulesState.loading && datasources && !datasources.length
|
||||
? 'No datasources'
|
||||
: datasourcesAndDefaultRulesState.getErrorMessage() || ''
|
||||
: '')
|
||||
}
|
||||
filterable
|
||||
filtered={datasourceFilter}
|
||||
|
|
|
@ -203,11 +203,11 @@ export class LookupsView extends React.PureComponent<LookupsViewProps, LookupsVi
|
|||
}));
|
||||
};
|
||||
|
||||
private async submitLookupEdit(updatelookupEditVersion: boolean) {
|
||||
private async submitLookupEdit(updateLookupVersion: boolean) {
|
||||
const { lookupEdit, isEdit } = this.state;
|
||||
if (!lookupEdit) return;
|
||||
|
||||
const version = updatelookupEditVersion ? new Date().toISOString() : lookupEdit.version;
|
||||
const version = updateLookupVersion ? new Date().toISOString() : lookupEdit.version;
|
||||
let endpoint = '/druid/coordinator/v1/lookups/config';
|
||||
const specJson: any = lookupEdit.spec;
|
||||
let dataJson: any;
|
||||
|
|
Loading…
Reference in New Issue