feat(dashboard): Native filters - add type to native filter configuration (#16549)

* iSort fixes

* Add type key to the new filters

* Fix wrong attribute

* PR comments

* PR comments

* Fix failing tests
diff --git a/superset-frontend/spec/fixtures/mockNativeFilters.ts b/superset-frontend/spec/fixtures/mockNativeFilters.ts
index e087072..6da3a35 100644
--- a/superset-frontend/spec/fixtures/mockNativeFilters.ts
+++ b/superset-frontend/spec/fixtures/mockNativeFilters.ts
@@ -17,6 +17,7 @@
  * under the License.
  */
 import { ExtraFormData } from '@superset-ui/core';
+import { NativeFilterType } from 'src/dashboard/components/nativeFilters/types';
 import { NativeFiltersState } from 'src/dashboard/reducers/types';
 import { DataMaskStateWithId } from '../../src/dataMask/types';
 
@@ -50,6 +51,7 @@
         enableEmptyFilter: false,
         inverseSelection: false,
       },
+      type: NativeFilterType.NATIVE_FILTER,
     },
     'NATIVE_FILTER-x9QPw0so1': {
       id: 'NATIVE_FILTER-x9QPw0so1',
@@ -78,6 +80,7 @@
         enableEmptyFilter: false,
         inverseSelection: false,
       },
+      type: NativeFilterType.NATIVE_FILTER,
     },
   },
 };
diff --git a/superset-frontend/spec/javascripts/dashboard/fixtures/mockNativeFilters.ts b/superset-frontend/spec/javascripts/dashboard/fixtures/mockNativeFilters.ts
index 0bf022d..1a309fd 100644
--- a/superset-frontend/spec/javascripts/dashboard/fixtures/mockNativeFilters.ts
+++ b/superset-frontend/spec/javascripts/dashboard/fixtures/mockNativeFilters.ts
@@ -16,8 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { DataMaskStateWithId } from 'src/dataMask/types';
+import { NativeFilterType } from 'src/dashboard/components/nativeFilters/types';
 import { NativeFiltersState } from 'src/dashboard/reducers/types';
+import { DataMaskStateWithId } from 'src/dataMask/types';
 
 export const mockDataMaskInfo: DataMaskStateWithId = {
   DefaultsID: {
@@ -66,6 +67,7 @@
         allowsMultipleValues: true,
         isRequired: false,
       },
+      type: NativeFilterType.NATIVE_FILTER,
     },
   },
 };
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
index e46d581..210739b 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
@@ -80,7 +80,7 @@
 } from './utils';
 import { useBackendFormUpdate, useDefaultValue } from './state';
 import { getFormData } from '../../utils';
-import { Filter } from '../../types';
+import { Filter, NativeFilterType } from '../../types';
 import getControlItemsMap from './getControlItemsMap';
 import FilterScope from './FilterScope/FilterScope';
 import RemovedFilter from './RemovedFilter';
@@ -685,6 +685,13 @@
       >
         <StyledContainer>
           <StyledFormItem
+            name={['filters', filterId, 'type']}
+            hidden
+            initialValue={NativeFilterType.NATIVE_FILTER}
+          >
+            <Input />
+          </StyledFormItem>
+          <StyledFormItem
             name={['filters', filterId, 'name']}
             label={<StyledLabel>{t('Filter name')}</StyledLabel>}
             initialValue={filterToEdit?.name}
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx
index f2f6298..53772cc 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/getControlItemsMap.test.tsx
@@ -16,13 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import userEvent from '@testing-library/user-event';
 import React from 'react';
 import { render, screen } from 'spec/helpers/testing-library';
-import userEvent from '@testing-library/user-event';
-import { Filter } from 'src/dashboard/components/nativeFilters/types';
 import { FormInstance } from 'src/common/components';
-import { getControlItems, setNativeFilterFieldValues } from './utils';
+import {
+  Filter,
+  NativeFilterType,
+} from 'src/dashboard/components/nativeFilters/types';
 import getControlItemsMap, { ControlItemsProps } from './getControlItemsMap';
+import { getControlItems, setNativeFilterFieldValues } from './utils';
 
 jest.mock('./utils', () => ({
   getControlItems: jest.fn(),
@@ -60,6 +63,7 @@
   filterType: '',
   targets: [{}],
   controlValues: {},
+  type: NativeFilterType.NATIVE_FILTER,
 };
 
 const createProps: () => ControlItemsProps = () => ({
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/types.ts b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/types.ts
index dd211ac..2dba43e 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/types.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/types.ts
@@ -17,7 +17,7 @@
  * under the License.
  */
 import { AdhocFilter, DataMask } from '@superset-ui/core';
-import { Scope } from '../types';
+import { NativeFilterType, Scope } from '../types';
 
 export interface NativeFiltersFormItem {
   scope: Scope;
@@ -44,6 +44,7 @@
   adhoc_filters?: AdhocFilter[];
   time_range?: string;
   granularity_sqla?: string;
+  type: NativeFilterType;
 }
 
 export interface NativeFiltersForm {
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/utils.ts b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/utils.ts
index c2307e4..de99034 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/utils.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/utils.ts
@@ -140,6 +140,7 @@
           : [],
         scope: formInputs.scope,
         sortMetric: formInputs.sortMetric,
+        type: formInputs.type,
       };
     });
 
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/types.ts b/superset-frontend/src/dashboard/components/nativeFilters/types.ts
index 3792d3e..6283398 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/types.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/types.ts
@@ -59,6 +59,12 @@
   requiredFirst?: boolean;
   tabsInScope?: string[];
   chartsInScope?: number[];
+  type: NativeFilterType;
 }
 
 export type FilterConfiguration = Filter[];
+
+export enum NativeFilterType {
+  NATIVE_FILTER = 'NATIVE_FILTER',
+  SECTION = 'SECTION',
+}
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/utils.ts b/superset-frontend/src/dashboard/components/nativeFilters/utils.ts
index 5dc81fd..60925c7 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/utils.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/utils.ts
@@ -47,6 +47,7 @@
   adhoc_filters,
   time_range,
   granularity_sqla,
+  type,
 }: Partial<Filter> & {
   datasetId?: number;
   inputRef?: RefObject<HTMLInputElement>;
@@ -86,6 +87,7 @@
     inView: true,
     viz_type: filterType,
     inputRef,
+    type,
   };
 };
 
diff --git a/superset/migrations/versions/021b81fe4fbb_add_type_to_native_filter_configuration.py b/superset/migrations/versions/021b81fe4fbb_add_type_to_native_filter_configuration.py
new file mode 100644
index 0000000..8238e8f
--- /dev/null
+++ b/superset/migrations/versions/021b81fe4fbb_add_type_to_native_filter_configuration.py
@@ -0,0 +1,121 @@
+# 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.
+"""Add type to native filter configuration
+
+Revision ID: 021b81fe4fbb
+Revises: 07071313dd52
+Create Date: 2021-08-31 11:37:40.604081
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = "021b81fe4fbb"
+down_revision = "07071313dd52"
+
+import json
+import logging
+
+import sqlalchemy as sa
+from alembic import op
+from sqlalchemy.ext.declarative.api import declarative_base
+
+from superset import db
+
+Base = declarative_base()
+
+logger = logging.getLogger("alembic")
+
+
+class Dashboard(Base):
+    __tablename__ = "dashboards"
+    id = sa.Column(sa.Integer, primary_key=True)
+    json_metadata = sa.Column(sa.Text)
+
+
+def upgrade():
+    logger.info("[AddTypeToNativeFilter] Starting upgrade")
+    bind = op.get_bind()
+    session = db.Session(bind=bind)
+
+    for dashboard in session.query(Dashboard).all():
+        logger.info("[AddTypeToNativeFilter] Updating Dashboard<pk:%s> ", dashboard.id)
+
+        if not dashboard.json_metadata:
+            logger.info(
+                "[AddTypeToNativeFilter] Skipping Dashboard<pk:%s> json_metadata is %s",
+                dashboard.id,
+                dashboard.json_metadata,
+            )
+            continue
+        try:
+            json_meta = json.loads(dashboard.json_metadata)
+        except:
+            logger.exception("[AddTypeToNativeFilter] Error loading json_metadata")
+            continue
+
+        if "native_filter_configuration" not in json_meta:
+            logger.info(
+                "[AddTypeToNativeFilter] Skipping Dashboard<pk:%s>."
+                " native_filter_configuration not found.",
+                dashboard.id,
+            )
+            continue
+
+        for native_filter in json_meta["native_filter_configuration"]:
+            native_filter["type"] = "NATIVE_FILTER"
+        dashboard.json_metadata = json.dumps(json_meta)
+
+    session.commit()
+    session.close()
+    logger.info("[AddTypeToNativeFilter] Done!")
+
+
+def downgrade():
+    logger.info("[RemoveTypeToNativeFilter] Starting downgrade")
+    bind = op.get_bind()
+    session = db.Session(bind=bind)
+
+    for dashboard in session.query(Dashboard).all():
+        logger.info(
+            "[RemoveTypeToNativeFilter] Updating Dashobard<pk:%s>", dashboard.id,
+        )
+        if not dashboard.json_metadata:
+            logger.info(
+                "[RemoveTypeToNativeFilter] Skipping Dashboard<pk:%s> json_metadata is %s",
+                dashboard.id,
+                dashboard.json_metadata,
+            )
+            continue
+        try:
+            json_meta = json.loads(dashboard.json_metadata)
+        except:
+            logger.exception("[RemoveTypeToNativeFilter] Error loading json_metadata")
+            continue
+
+        if "native_filter_configuration" not in json_meta:
+            logger.info(
+                "[RemoveTypeToNativeFilter] Skipping Dashboard<pk:%s>."
+                " native_filter_configuration not found.",
+                dashboard.id,
+            )
+            continue
+        for native_filter in json_meta["native_filter_configuration"]:
+            native_filter.pop("type", None)
+        dashboard.json_metadata = json.dumps(json_meta)
+    session.commit()
+    session.close()
+    logger.info("[RemoveTypeToNativeFilter] Done!")