Web console: fix lookup edit dialog version setting (#10461)

* fix lookup edit dialog

* update snapshots

* clean up test
This commit is contained in:
Vadim Ogievetsky 2020-10-03 08:35:20 -07:00 committed by GitHub
parent d11537b5f7
commit f77c16bc6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 584 additions and 574 deletions

View File

@ -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}
/>
);
}

View File

@ -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>
`;

View File

@ -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();
});
});

View File

@ -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>
);
});

View File

@ -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"
>

View File

@ -21,7 +21,7 @@
height: 80vh;
}
.tabs {
.form-json-selector {
margin: 15px;
}

View File

@ -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

View File

@ -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"
>

View File

@ -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 {

View File

@ -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']}
/>,
);

View File

@ -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} />

View File

@ -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}

View File

@ -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;