Web console: fix lookup edit dialog version setting (#10461)
* fix lookup edit dialog
* update snapshots
* clean up test
diff --git a/web-console/src/components/auto-form/auto-form.tsx b/web-console/src/components/auto-form/auto-form.tsx
index 59561ac..ce26cad 100644
--- a/web-console/src/components/auto-form/auto-form.tsx
+++ b/web-console/src/components/auto-form/auto-form.tsx
@@ -50,6 +50,7 @@
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 @@
value={deepGet(model as any, field.name)}
onChange={(v: any) => this.fieldChange(field, v)}
placeholder={AutoForm.evaluateFunctor(field.placeholder, model, '')}
+ height={field.height}
/>
);
}
diff --git a/web-console/src/components/form-json-selector/__snapshots__/form-json-selector.spec.tsx.snap b/web-console/src/components/form-json-selector/__snapshots__/form-json-selector.spec.tsx.snap
new file mode 100644
index 0000000..d2ec216
--- /dev/null
+++ b/web-console/src/components/form-json-selector/__snapshots__/form-json-selector.spec.tsx.snap
@@ -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>
+`;
diff --git a/web-console/src/components/form-json-selector/form-json-selector.spec.tsx b/web-console/src/components/form-json-selector/form-json-selector.spec.tsx
new file mode 100644
index 0000000..ae7c3a9
--- /dev/null
+++ b/web-console/src/components/form-json-selector/form-json-selector.spec.tsx
@@ -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();
+ });
+});
diff --git a/web-console/src/components/form-json-selector/form-json-selector.tsx b/web-console/src/components/form-json-selector/form-json-selector.tsx
new file mode 100644
index 0000000..4999826
--- /dev/null
+++ b/web-console/src/components/form-json-selector/form-json-selector.tsx
@@ -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>
+ );
+});
diff --git a/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap b/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap
index 2b1635e..57ead47 100644
--- a/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/compaction-dialog/__snapshots__/compaction-dialog.spec.tsx.snap
@@ -8,24 +8,10 @@
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 @@
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 @@
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 @@
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"
>
diff --git a/web-console/src/dialogs/compaction-dialog/compaction-dialog.scss b/web-console/src/dialogs/compaction-dialog/compaction-dialog.scss
index 65606ba..f5cd57f 100644
--- a/web-console/src/dialogs/compaction-dialog/compaction-dialog.scss
+++ b/web-console/src/dialogs/compaction-dialog/compaction-dialog.scss
@@ -21,7 +21,7 @@
height: 80vh;
}
- .tabs {
+ .form-json-selector {
margin: 15px;
}
diff --git a/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx b/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx
index 4a2611b..44bee9a 100644
--- a/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx
+++ b/web-console/src/dialogs/compaction-dialog/compaction-dialog.tsx
@@ -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 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 @@
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
diff --git a/web-console/src/dialogs/lookup-edit-dialog/__snapshots__/lookup-edit-dialog.spec.tsx.snap b/web-console/src/dialogs/lookup-edit-dialog/__snapshots__/lookup-edit-dialog.spec.tsx.snap
index 1fe5202..5349950 100644
--- a/web-console/src/dialogs/lookup-edit-dialog/__snapshots__/lookup-edit-dialog.spec.tsx.snap
+++ b/web-console/src/dialogs/lookup-edit-dialog/__snapshots__/lookup-edit-dialog.spec.tsx.snap
@@ -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 @@
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"
>
diff --git a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.scss b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.scss
index 7ee469b..e42914f 100644
--- a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.scss
+++ b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.scss
@@ -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 {
diff --git a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.spec.tsx b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.spec.tsx
index a314302..f9eebd8 100644
--- a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.spec.tsx
+++ b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.spec.tsx
@@ -21,7 +21,7 @@
import { isLookupSubmitDisabled, LookupEditDialog } from './lookup-edit-dialog';
-describe('lookup edit dialog', () => {
+describe('LookupEditDialog', () => {
it('matches snapshot', () => {
const lookupEditDialog = shallow(
<LookupEditDialog
@@ -31,9 +31,9 @@
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']}
/>,
);
diff --git a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx
index fe203fd..24eb832 100644
--- a/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx
+++ b/web-console/src/dialogs/lookup-edit-dialog/lookup-edit-dialog.tsx
@@ -25,9 +25,13 @@
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 @@
{
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 @@
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 @@
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} />
diff --git a/web-console/src/views/datasource-view/datasource-view.tsx b/web-console/src/views/datasource-view/datasource-view.tsx
index 9f81a03..02397c6 100644
--- a/web-console/src/views/datasource-view/datasource-view.tsx
+++ b/web-console/src/views/datasource-view/datasource-view.tsx
@@ -893,9 +893,10 @@
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}
diff --git a/web-console/src/views/lookups-view/lookups-view.tsx b/web-console/src/views/lookups-view/lookups-view.tsx
index 46eb421..3666263 100644
--- a/web-console/src/views/lookups-view/lookups-view.tsx
+++ b/web-console/src/views/lookups-view/lookups-view.tsx
@@ -203,11 +203,11 @@
}));
};
- 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;