From a9c887cd169e284d84aaf2ffbab395a2da9035bd Mon Sep 17 00:00:00 2001 From: Vijay Prasanna Date: Thu, 20 Oct 2022 17:03:53 +0530 Subject: [PATCH] tests (console): add tests for `getTableName` util function PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6375 GitOrigin-RevId: 82acd22d60a878c6619158637290c923cd8b43f0 --- .../components/RunQuery/hooks/useTableName.ts | 3 +- .../features/Data/ManageTable/ManageTable.tsx | 2 +- .../Data/TrackTables/hooks/useTables.ts | 47 ++--------- console/src/features/Data/index.tsx | 2 +- .../parts/SourceTable.tsx | 3 +- .../parts/TargetTable.tsx | 3 +- .../common/getTableName/getTableName.test.ts | 78 +++++++++++++++++++ .../common/getTableName/getTableName.ts | 45 +++++++++++ .../DataSource/common/getTableName/index.ts | 1 + console/src/features/DataSource/index.ts | 4 +- 10 files changed, 141 insertions(+), 47 deletions(-) create mode 100644 console/src/features/DataSource/common/getTableName/getTableName.test.ts create mode 100644 console/src/features/DataSource/common/getTableName/getTableName.ts create mode 100644 console/src/features/DataSource/common/getTableName/index.ts diff --git a/console/src/features/BrowseRows/components/RunQuery/hooks/useTableName.ts b/console/src/features/BrowseRows/components/RunQuery/hooks/useTableName.ts index a2abd430c4b..7b6f44db302 100644 --- a/console/src/features/BrowseRows/components/RunQuery/hooks/useTableName.ts +++ b/console/src/features/BrowseRows/components/RunQuery/hooks/useTableName.ts @@ -1,6 +1,5 @@ import { useIsUnmounted } from '@/components/Services/Data'; -import { getTableName } from '@/features/Data'; -import { DataSource } from '@/features/DataSource'; +import { DataSource, getTableName } from '@/features/DataSource'; import { useHttpClient } from '@/features/Network'; import { useState, useEffect } from 'react'; diff --git a/console/src/features/Data/ManageTable/ManageTable.tsx b/console/src/features/Data/ManageTable/ManageTable.tsx index 5f42047cfc8..ef78fe85edc 100644 --- a/console/src/features/Data/ManageTable/ManageTable.tsx +++ b/console/src/features/Data/ManageTable/ManageTable.tsx @@ -1,11 +1,11 @@ import { BrowseRowsContainer } from '@/features/BrowseRows'; +import { getTableName } from '@/features/DataSource'; import { Table } from '@/features/MetadataAPI'; import { IndicatorCard } from '@/new-components/IndicatorCard'; import { Tabs } from '@/new-components/Tabs'; import React, { useState } from 'react'; import { useDatabaseHierarchy } from '../hooks'; import { ModifyTable } from '../ModifyTable/ModifyTable'; -import { getTableName } from '../TrackTables/hooks/useTables'; import { Breadcrumbs, TableName } from './parts'; export interface ManageTableProps { diff --git a/console/src/features/Data/TrackTables/hooks/useTables.ts b/console/src/features/Data/TrackTables/hooks/useTables.ts index 5f377c69c98..d952171f747 100644 --- a/console/src/features/Data/TrackTables/hooks/useTables.ts +++ b/console/src/features/Data/TrackTables/hooks/useTables.ts @@ -1,6 +1,11 @@ -import type { IntrospectedTable } from '@/features/DataSource'; -import { DataSource, exportMetadata, Feature } from '@/features/DataSource'; -import { MetadataTable, Table } from '@/features/MetadataAPI'; +import { + getTableName, + IntrospectedTable, + DataSource, + exportMetadata, + Feature, +} from '@/features/DataSource'; +import { MetadataTable } from '@/features/MetadataAPI'; import { useHttpClient } from '@/features/Network'; import { useQuery } from 'react-query'; import type { TrackableTable } from '../types'; @@ -9,42 +14,6 @@ export type UseTablesProps = { dataSourceName: string; }; -export const getTableName = ( - table: Table, - databaseHierarchy: string[] -): string => { - if (databaseHierarchy.length === 0) { - if (!Array.isArray(table)) return ''; - - const result = table.reduce((acc, item) => { - if (typeof item === 'string') acc.push(item); - return acc; - }, []); - - return result.join('.'); - } - - if (table && typeof table === 'object') { - const flatJsonTableDefinition = Object.entries(table).reduce< - Record - >((acc, item) => { - const [key, value] = item; - if (typeof key === 'string' && typeof value === 'string') - acc[key] = value; - return acc; - }, {}); - - const tableName = databaseHierarchy - .map(key => { - return flatJsonTableDefinition[key]; - }) - .join('.'); - return tableName; - } - - return JSON.stringify(table); -}; - const getTrackableTables = ( trackedTables: MetadataTable[], introspectedTables: IntrospectedTable[], diff --git a/console/src/features/Data/index.tsx b/console/src/features/Data/index.tsx index 4e593b795f9..8d1ff743a54 100644 --- a/console/src/features/Data/index.tsx +++ b/console/src/features/Data/index.tsx @@ -1,6 +1,6 @@ export * from './ManageContainer'; export * from './components'; export * from './hooks'; -export { getTableName, tablesQueryKey } from './TrackTables/hooks/useTables'; +export { tablesQueryKey } from './TrackTables/hooks/useTables'; export { useTrackTable } from './TrackTables/hooks/useTrackTable'; export { useMetadataSource } from './TrackTables/hooks/useMetadataSource'; diff --git a/console/src/features/DataRelationships/components/ManualLocalRelationshipWidget/parts/SourceTable.tsx b/console/src/features/DataRelationships/components/ManualLocalRelationshipWidget/parts/SourceTable.tsx index ab1d7ba5ded..48778498e1d 100644 --- a/console/src/features/DataRelationships/components/ManualLocalRelationshipWidget/parts/SourceTable.tsx +++ b/console/src/features/DataRelationships/components/ManualLocalRelationshipWidget/parts/SourceTable.tsx @@ -1,4 +1,5 @@ -import { useDatabaseHierarchy, getTableName } from '@/features/Data'; +import { useDatabaseHierarchy } from '@/features/Data'; +import { getTableName } from '@/features/DataSource'; import { Table } from '@/features/MetadataAPI'; import { InputField, Select } from '@/new-components/Form'; import React from 'react'; diff --git a/console/src/features/DataRelationships/components/ManualLocalRelationshipWidget/parts/TargetTable.tsx b/console/src/features/DataRelationships/components/ManualLocalRelationshipWidget/parts/TargetTable.tsx index e7f45456755..c7af365df9e 100644 --- a/console/src/features/DataRelationships/components/ManualLocalRelationshipWidget/parts/TargetTable.tsx +++ b/console/src/features/DataRelationships/components/ManualLocalRelationshipWidget/parts/TargetTable.tsx @@ -1,4 +1,5 @@ -import { useDatabaseHierarchy, getTableName } from '@/features/Data'; +import { useDatabaseHierarchy } from '@/features/Data'; +import { getTableName } from '@/features/DataSource'; import { Table } from '@/features/MetadataAPI'; import { InputField, Select } from '@/new-components/Form'; import React from 'react'; diff --git a/console/src/features/DataSource/common/getTableName/getTableName.test.ts b/console/src/features/DataSource/common/getTableName/getTableName.test.ts new file mode 100644 index 00000000000..0fe7a7c3291 --- /dev/null +++ b/console/src/features/DataSource/common/getTableName/getTableName.test.ts @@ -0,0 +1,78 @@ +import { getTableName } from './getTableName'; + +describe('getTableName', () => { + test.each` + table | databaseHierarchy | expectedName + ${{ name: 'Album', schema: 'public' }} | ${['schema', 'name']} | ${'public.Album'} + ${{ name: 'Album', dataset: 'public' }} | ${['dataset', 'name']} | ${'public.Album'} + ${['public', 'Album']} | ${[]} | ${'public.Album'} + `( + 'when invoked for $table & $hierarchy, should return $expectedName', + ({ table, databaseHierarchy, expectedName }) => { + console.log(table, databaseHierarchy); + const result = getTableName(table, databaseHierarchy); + expect(result).toBe(expectedName); + } + ); + + it('should throw error, if hierachy is wrong', () => { + try { + // TS error is hidden because we're checking for an error + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const result = getTableName({ name: 'Album', schema: 'public' }, [ + 'dataset', + 'name', + ]); + } catch (err: unknown) { + expect((err as Error).message).toEqual('unable to find hierachy value'); + } + }); + + it('should throw error, if hierachy is empty and the table is an object', () => { + try { + // TS error is hidden because we're checking for an error + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const result = getTableName({ name: 'Album', schema: 'public' }, []); + } catch (err: unknown) { + expect((err as Error).message).toEqual('No database hierarchy found'); + } + }); + + test.each` + tableObject | databaseHierarchy + ${null} | ${['schema', 'name']} + ${undefined} | ${['dataset', 'name']} + `( + 'should throw error, if table is %s', + ({ tableObject, databaseHierarchy }) => { + try { + // TS error is hidden because we're checking for an error + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const result = getTableName(tableObject, databaseHierarchy); + } catch (err: unknown) { + expect((err as Error).message).toEqual( + 'Table cannot be null or undefined' + ); + } + } + ); + + it.each` + tableObject | databaseHierarchy + ${1} | ${[]} + ${123.5} | ${[]} + `( + 'should throw error, if table is %s', + ({ tableObject, databaseHierarchy }) => { + try { + // TS error is hidden because we're checking for an error + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const result = getTableName(tableObject, databaseHierarchy); + } catch (err: unknown) { + expect((err as Error).message).toEqual( + 'Table name could be generated for the given table type' + ); + } + } + ); +}); diff --git a/console/src/features/DataSource/common/getTableName/getTableName.ts b/console/src/features/DataSource/common/getTableName/getTableName.ts new file mode 100644 index 00000000000..bad85acbbd4 --- /dev/null +++ b/console/src/features/DataSource/common/getTableName/getTableName.ts @@ -0,0 +1,45 @@ +import { Table } from '@/features/MetadataAPI'; + +export const getTableName = ( + table: Table, + databaseHierarchy: string[] +): string => { + if (table === null || table === undefined) + throw Error('Table cannot be null or undefined'); + + if (typeof table === 'string') return table; + + if (Array.isArray(table)) { + // verify if every entry in the array is a string + const result = table.map(item => { + if (typeof item === 'string') return item; + throw Error('Non string values found in table'); + }); + + return result.join('.'); + } + + if (typeof table === 'object') { + if (!databaseHierarchy || !databaseHierarchy.length) + throw Error('No database hierarchy found'); + + const flatJsonTableDefinition = Object.entries(table).reduce< + Record + >((acc, item) => { + const [key, value] = item; + if (typeof key === 'string' && typeof value === 'string') + acc[key] = value; + return acc; + }, {}); + + const tableName = databaseHierarchy + .map(key => { + if (flatJsonTableDefinition[key]) return flatJsonTableDefinition[key]; + throw Error('unable to find hierachy value'); + }) + .join('.'); + return tableName; + } + + throw Error('Table name could be generated for the given table type'); +}; diff --git a/console/src/features/DataSource/common/getTableName/index.ts b/console/src/features/DataSource/common/getTableName/index.ts new file mode 100644 index 00000000000..7e0aa4c5d25 --- /dev/null +++ b/console/src/features/DataSource/common/getTableName/index.ts @@ -0,0 +1 @@ +export { getTableName } from './getTableName'; diff --git a/console/src/features/DataSource/index.ts b/console/src/features/DataSource/index.ts index 5a61778ebf0..91d4ff9f5cb 100644 --- a/console/src/features/DataSource/index.ts +++ b/console/src/features/DataSource/index.ts @@ -6,7 +6,6 @@ import { z } from 'zod'; import { bigquery } from './bigquery'; import { citus } from './citus'; import { cockroach } from './cockroach'; -import * as utils from './common/utils'; import { gdc } from './gdc'; import { mssql } from './mssql'; import { postgres, PostgresTable } from './postgres'; @@ -37,6 +36,7 @@ import { RunSQLResponse, } from './api'; import { getAllSourceKinds } from './common/getAllSourceKinds'; +import { getTableName } from './common/getTableName'; export enum Feature { NotImplemented = 'Not Implemented', @@ -407,7 +407,7 @@ export * from './types'; export { PostgresTable, exportMetadata, - utils, + getTableName, RunSQLResponse, getDriverPrefix, };