This is an automated email from the ASF dual-hosted git repository.

beto pushed a commit to branch default-schema-catalog
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 62291a997a91e35719ffa7f0c4e577d5179c83b3
Author: Beto Dealmeida <[email protected]>
AuthorDate: Mon Jul 8 17:54:18 2024 -0400

    feat: auto-select default catalog and schema
---
 .../src/components/DatabaseSelector/index.tsx      | 16 ++++++++---
 .../src/hooks/apiResources/catalogs.ts             | 32 ++++++++++++++++++----
 .../src/hooks/apiResources/schemas.ts              | 32 ++++++++++++++++++----
 superset/databases/api.py                          | 11 ++++----
 superset/databases/schemas.py                      |  8 ++++++
 5 files changed, 80 insertions(+), 19 deletions(-)

diff --git a/superset-frontend/src/components/DatabaseSelector/index.tsx 
b/superset-frontend/src/components/DatabaseSelector/index.tsx
index 4cf8411568..8b8dbee982 100644
--- a/superset-frontend/src/components/DatabaseSelector/index.tsx
+++ b/superset-frontend/src/components/DatabaseSelector/index.tsx
@@ -266,12 +266,17 @@ export default function DatabaseSelector({
   } = useSchemas({
     dbId: currentDb?.value,
     catalog: currentCatalog?.value,
-    onSuccess: (schemas, isFetched) => {
+    onSuccess: (schemas, isFetched, defaultSchema) => {
       if (schemas.length === 1) {
         changeSchema(schemas[0]);
       } else if (
-        !schemas.find(schemaOption => schemaRef.current === schemaOption.value)
+        !schemas.find(
+          schemaOption => schemaRef.current === schemaOption.value,
+        ) &&
+        defaultSchema
       ) {
+        changeSchema(defaultSchema);
+      } else {
         changeSchema(undefined);
       }
 
@@ -298,7 +303,7 @@ export default function DatabaseSelector({
     refetch: refetchCatalogs,
   } = useCatalogs({
     dbId: showCatalogSelector ? currentDb?.value : undefined,
-    onSuccess: (catalogs, isFetched) => {
+    onSuccess: (catalogs, isFetched, defaultCatalog) => {
       if (!showCatalogSelector) {
         changeCatalog(null);
       } else if (catalogs.length === 1) {
@@ -306,8 +311,11 @@ export default function DatabaseSelector({
       } else if (
         !catalogs.find(
           catalogOption => catalogRef.current === catalogOption.value,
-        )
+        ) &&
+        defaultCatalog
       ) {
+        changeCatalog(defaultCatalog);
+      } else {
         changeCatalog(undefined);
       }
 
diff --git a/superset-frontend/src/hooks/apiResources/catalogs.ts 
b/superset-frontend/src/hooks/apiResources/catalogs.ts
index 5203797156..d98302bf45 100644
--- a/superset-frontend/src/hooks/apiResources/catalogs.ts
+++ b/superset-frontend/src/hooks/apiResources/catalogs.ts
@@ -26,10 +26,19 @@ export type CatalogOption = {
   title: string;
 };
 
+export type CatalogResponse = {
+  catalogs: CatalogOption[];
+  defaultCatalog: CatalogOption | null;
+};
+
 export type FetchCatalogsQueryParams = {
   dbId?: string | number;
   forceRefresh: boolean;
-  onSuccess?: (data: CatalogOption[], isRefetched: boolean) => void;
+  onSuccess?: (
+    data: CatalogOption[],
+    isRefetched: boolean,
+    defaultCatalog: CatalogOption | null,
+  ) => void;
   onError?: () => void;
 };
 
@@ -37,19 +46,28 @@ type Params = Omit<FetchCatalogsQueryParams, 
'forceRefresh'>;
 
 const catalogApi = api.injectEndpoints({
   endpoints: builder => ({
-    catalogs: builder.query<CatalogOption[], FetchCatalogsQueryParams>({
+    catalogs: builder.query<CatalogResponse, FetchCatalogsQueryParams>({
       providesTags: [{ type: 'Catalogs', id: 'LIST' }],
       query: ({ dbId, forceRefresh }) => ({
         endpoint: `/api/v1/database/${dbId}/catalogs/`,
         urlParams: {
           force: forceRefresh,
         },
-        transformResponse: ({ json }: JsonResponse) =>
-          json.result.sort().map((value: string) => ({
+        transformResponse: ({ json }: JsonResponse) => ({
+          catalog: json.result.sort().map((value: string) => ({
             value,
             label: value,
             title: value,
           })),
+          defaultCatalog:
+            json.result.default !== null
+              ? ({
+                  value: json.result.default,
+                  label: json.result.default,
+                  title: json.result.default,
+                } as CatalogOption)
+              : null,
+        }),
       }),
       serializeQueryArgs: ({ queryArgs: { dbId } }) => ({
         dbId,
@@ -82,7 +100,11 @@ export function useCatalogs(options: Params) {
       if (dbId && (!result.currentData || forceRefresh)) {
         trigger({ dbId, forceRefresh }).then(({ isSuccess, isError, data }) => 
{
           if (isSuccess) {
-            onSuccess?.(data || EMPTY_CATALOGS, forceRefresh);
+            onSuccess?.(
+              data.catalogs || EMPTY_CATALOGS,
+              forceRefresh,
+              data.defaultCatalog,
+            );
           }
           if (isError) {
             onError?.();
diff --git a/superset-frontend/src/hooks/apiResources/schemas.ts 
b/superset-frontend/src/hooks/apiResources/schemas.ts
index e60e62a7fb..05aebbb006 100644
--- a/superset-frontend/src/hooks/apiResources/schemas.ts
+++ b/superset-frontend/src/hooks/apiResources/schemas.ts
@@ -26,11 +26,20 @@ export type SchemaOption = {
   title: string;
 };
 
+export type SchemaResponse = {
+  schemas: SchemaOption[];
+  defaultSchema: SchemaOption | null;
+};
+
 export type FetchSchemasQueryParams = {
   dbId?: string | number;
   catalog?: string;
   forceRefresh: boolean;
-  onSuccess?: (data: SchemaOption[], isRefetched: boolean) => void;
+  onSuccess?: (
+    data: SchemaOption[],
+    isRefetched: boolean,
+    defaultSchema: SchemaOption | null,
+  ) => void;
   onError?: () => void;
 };
 
@@ -38,7 +47,7 @@ type Params = Omit<FetchSchemasQueryParams, 'forceRefresh'>;
 
 const schemaApi = api.injectEndpoints({
   endpoints: builder => ({
-    schemas: builder.query<SchemaOption[], FetchSchemasQueryParams>({
+    schemas: builder.query<SchemaResponse, FetchSchemasQueryParams>({
       providesTags: [{ type: 'Schemas', id: 'LIST' }],
       query: ({ dbId, catalog, forceRefresh }) => ({
         endpoint: `/api/v1/database/${dbId}/schemas/`,
@@ -47,12 +56,21 @@ const schemaApi = api.injectEndpoints({
           force: forceRefresh,
           ...(catalog !== undefined && { catalog }),
         },
-        transformResponse: ({ json }: JsonResponse) =>
-          json.result.sort().map((value: string) => ({
+        transformResponse: ({ json }: JsonResponse) => ({
+          schemas: json.result.sort().map((value: string) => ({
             value,
             label: value,
             title: value,
           })),
+          defaultSchema:
+            json.result.default !== null
+              ? ({
+                  value: json.result.default,
+                  label: json.result.default,
+                  title: json.result.default,
+                } as SchemaOption)
+              : null,
+        }),
       }),
       serializeQueryArgs: ({ queryArgs: { dbId, catalog } }) => ({
         dbId,
@@ -91,7 +109,11 @@ export function useSchemas(options: Params) {
         trigger({ dbId, catalog, forceRefresh }).then(
           ({ isSuccess, isError, data }) => {
             if (isSuccess) {
-              onSuccess?.(data || EMPTY_SCHEMAS, forceRefresh);
+              onSuccess?.(
+                data.schemas || EMPTY_SCHEMAS,
+                forceRefresh,
+                data.defaultSchema,
+              );
             }
             if (isError) {
               onError?.();
diff --git a/superset/databases/api.py b/superset/databases/api.py
index 3a672eb766..1a2fc0e6f9 100644
--- a/superset/databases/api.py
+++ b/superset/databases/api.py
@@ -665,7 +665,8 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
                 database,
                 catalogs,
             )
-            return self.response(200, result=list(catalogs))
+            default_catalog = database.get_default_catalog()
+            return self.response(200, result=list(catalogs), 
default=default_catalog)
         except OperationalError:
             return self.response(
                 500,
@@ -732,7 +733,9 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
                 catalog,
                 schemas,
             )
-            return self.response(200, result=list(schemas))
+            default_catalog = database.get_default_catalog()
+            default_schema = database.get_default_schema(default_catalog)
+            return self.response(200, result=list(schemas), 
default=default_schema)
         except OperationalError:
             return self.response(
                 500, message="There was an error connecting to the database"
@@ -1885,9 +1888,7 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
     @protect()
     @statsd_metrics
     @event_logger.log_this_with_context(
-        action=lambda self,
-        *args,
-        **kwargs: f"{self.__class__.__name__}.columnar_upload",
+        action=lambda self, *args, **kwargs: 
f"{self.__class__.__name__}.columnar_upload",
         log_to_statsd=False,
     )
     @requires_form_data
diff --git a/superset/databases/schemas.py b/superset/databases/schemas.py
index 7dbaf0243a..d099670e91 100644
--- a/superset/databases/schemas.py
+++ b/superset/databases/schemas.py
@@ -721,12 +721,20 @@ class SchemasResponseSchema(Schema):
     result = fields.List(
         fields.String(metadata={"description": "A database schema name"})
     )
+    default = fields.String(
+        allow_none=True,
+        metadata={"description": "The default schema"},
+    )
 
 
 class CatalogsResponseSchema(Schema):
     result = fields.List(
         fields.String(metadata={"description": "A database catalog name"})
     )
+    default = fields.String(
+        allow_none=True,
+        metadata={"description": "The default catalog"},
+    )
 
 
 class DatabaseTablesResponse(Schema):

Reply via email to