Add support for Graphql Customizations in Browse Rows

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9845
Co-authored-by: Matthew Goodwin <49927862+m4ttheweric@users.noreply.github.com>
GitOrigin-RevId: c9daa739437df20daa3db7ba912dd898274e7303
This commit is contained in:
Luca Restagno 2023-07-19 16:52:38 +02:00 committed by hasura-bot
parent 47a2eaf686
commit 38b3c5258d
16 changed files with 909 additions and 221 deletions

View File

@ -15,7 +15,7 @@ import {
findTableFromRel,
isFeatureSupported,
} from '../../../../dataSources';
import { getTableConfiguration } from './utils';
import { getTableConfiguration, getTableCustomization } from './utils';
import { reloadDataSource } from '../../../../metadata/actions';
import { updateLimit } from './ViewAction.utils';
@ -73,11 +73,17 @@ const getConfiguration = (tables, sources) => {
)?.configuration;
};
const getCustomization = (tables, sources) => {
const { currentDataSource } = tables;
return sources?.find(s => s.name === currentDataSource)?.customization;
};
const vMakeRowsRequest = () => {
return (dispatch, getState) => {
const { tables, metadata } = getState();
const sources = metadata.metadataObject?.sources;
const tableConfiguration = getTableConfiguration(tables, sources);
const dataSourceCustomization = getCustomization(tables, sources);
const headers = dataHeaders(getState);
dispatch({ type: V_REQUEST_PROGRESS, data: true });
@ -86,7 +92,11 @@ const vMakeRowsRequest = () => {
const options = {
method: 'POST',
body: JSON.stringify(
getTableRowRequestBody({ tables, tableConfiguration })
getTableRowRequestBody({
tables,
tableConfiguration,
dataSourceCustomization,
})
),
headers,
credentials: globalCookiePolicy,
@ -117,10 +127,13 @@ const vMakeRowsRequest = () => {
return;
}
const { currentSchema, currentTable: originalTable } = tables;
const dataSourceCustomization = getCustomization(tables, sources);
const { rows, estimatedCount } = processTableRowData(data, {
currentSchema,
originalTable,
tableConfiguration,
dataSourceCustomization,
});
const currentTable = getState().tables.currentTable;
@ -153,12 +166,18 @@ const vMakeExportRequest = () => {
const headers = dataHeaders(getState);
const sources = metadata.metadataObject?.sources;
const tableConfiguration = getConfiguration(tables, sources);
const dataSourceCustomization = getCustomization(tables, sources);
const { endpoint, getTableRowRequestBody, processTableRowData } =
dataSource.generateTableRowRequest();
const options = {
method: 'POST',
body: JSON.stringify(
getTableRowRequestBody({ tables, tableConfiguration, isExport: true })
getTableRowRequestBody({
tables,
tableConfiguration,
isExport: true,
dataSourceCustomization,
})
),
headers,
credentials: globalCookiePolicy,
@ -195,10 +214,15 @@ const vMakeCountRequest = () => {
const { tables, metadata } = getState();
const sources = metadata.metadataObject?.sources;
const tableConfiguration = getConfiguration(tables, sources);
const dataSourceCustomization = getCustomization(tables, sources);
const { endpoint, getRowsCountRequestBody, processCount } =
dataSource.generateRowsCountRequest();
const requestBody = getRowsCountRequestBody({ tables, tableConfiguration });
const requestBody = getRowsCountRequestBody({
tables,
tableConfiguration,
dataSourceCustomization,
});
const options = {
method: 'POST',
@ -211,11 +235,13 @@ const vMakeCountRequest = () => {
data => {
if (!isEmpty(data)) {
const { currentTable: originalTable, currentSchema } = tables;
const dataSourceCustomization = getCustomization(tables, sources);
const count = processCount({
data,
currentSchema,
originalTable,
tableConfiguration,
dataSourceCustomization,
});
const currentTable = getState().tables.currentTable;
// in case table has changed before count load
@ -281,6 +307,7 @@ const deleteItem = (pkClause, tableName, tableSchema) => {
const sources = metadata.metadataObject?.sources;
const tableConfiguration = getTableConfiguration(tables, sources);
const tableCustomization = getTableCustomization(tables, sources);
const { endpoint, getDeleteRowRequestBody, processDeleteRowData } =
dataSource.generateDeleteRowRequest();
@ -291,6 +318,7 @@ const deleteItem = (pkClause, tableName, tableSchema) => {
source,
columnInfo: columnTypeInfo,
tableConfiguration,
dataSourceCustomization: tableCustomization,
});
const options = {
@ -302,11 +330,9 @@ const deleteItem = (pkClause, tableName, tableSchema) => {
dispatch(requestAction(endpoint, options)).then(
data => {
try {
const affectedRows = processDeleteRowData(
data,
tableName,
tableSchema
);
const affectedRows = processDeleteRowData(data, {
dataSourceCustomization: tableCustomization,
});
dispatch(vMakeTableRequests());
dispatch(
showSuccessNotification(
@ -350,6 +376,7 @@ const deleteItems = (pkClauses, tableName, tableSchema) => {
const sources = metadata.metadataObject?.sources;
const tableConfiguration = getTableConfiguration(tables, sources);
const tableCustomization = getTableCustomization(tables, sources);
const reqBody = getBulkDeleteRowRequestBody({
pkClauses,
@ -358,6 +385,7 @@ const deleteItems = (pkClauses, tableName, tableSchema) => {
source,
columnInfo: columnTypeInfo,
tableConfiguration,
dataSourceCustomization: tableCustomization,
});
const options = {
@ -370,11 +398,9 @@ const deleteItems = (pkClauses, tableName, tableSchema) => {
dispatch(requestAction(endpoint, options)).then(
data => {
try {
const affected = processBulkDeleteRowData(
data,
tableName,
tableSchema
);
const affected = processBulkDeleteRowData(data, {
dataSourceCustomization: tableCustomization,
});
dispatch(vMakeTableRequests());
dispatch(
showSuccessNotification(

View File

@ -18,7 +18,14 @@ export const isTableWithPK = (
);
};
export const getTableConfiguration = (
const getSource = (
currentDataSource: string,
sources: MetadataDataSource[]
) => {
return sources?.find(s => s.name === currentDataSource);
};
const getTable = (
tables: ReduxState['tables'],
sources: MetadataDataSource[]
) => {
@ -27,11 +34,26 @@ export const getTableConfiguration = (
currentTable: originalTable,
currentDataSource,
} = tables;
return sources
?.find(s => s.name === currentDataSource)
?.tables.find(
t => originalTable === t.table.name && currentSchema === t.table.schema
)?.configuration;
return getSource(currentDataSource, sources)?.tables.find(
t => originalTable === t.table.name && currentSchema === t.table.schema
);
};
export const getTableConfiguration = (
tables: ReduxState['tables'],
sources: MetadataDataSource[]
) => {
return getTable(tables, sources)?.configuration;
};
export const getTableCustomization = (
tables: ReduxState['tables'],
sources: MetadataDataSource[]
) => {
const { currentDataSource } = tables;
return getSource(currentDataSource, sources)?.customization ?? {};
};
export const compareRows = (

View File

@ -15,7 +15,10 @@ import {
import { getEnumOptionsQuery } from '../../../Common/utils/v1QueryUtils';
import { isArray, isStringArray } from '../../../Common/utils/jsUtils';
import { generateTableDef } from '../../../../dataSources';
import { getTableConfiguration } from '../TableBrowseRows/utils';
import {
getTableConfiguration,
getTableCustomization,
} from '../TableBrowseRows/utils';
const E_SET_EDITITEM = 'EditItem/E_SET_EDITITEM';
const E_ONGOING_REQ = 'EditItem/E_ONGOING_REQ';
@ -35,6 +38,7 @@ const editItem = (tableName, colValues) => {
const { tables, metadata } = getState();
const sources = metadata.metadataObject?.sources;
const tableConfiguration = getTableConfiguration(tables, sources);
const tableCustomization = getTableCustomization(tables, sources);
/* Type all the values correctly */
const { currentSchema, allSchemas, currentDataSource } = tables;
@ -133,6 +137,7 @@ const editItem = (tableName, colValues) => {
source: currentDataSource,
tableDef,
tableConfiguration,
dataSourceCustomization: tableCustomization,
set: _setObject,
defaultArray: _defaultArray,
where: tables.update.pkClause,
@ -153,7 +158,12 @@ const editItem = (tableName, colValues) => {
showSuccessNotification(
'Edited!',
'Affected rows: ' +
processEditData({ data, tableDef, tableConfiguration })
processEditData({
data,
tableDef,
tableConfiguration,
dataSourceCustomization: tableCustomization,
})
)
);
},

View File

@ -21,7 +21,10 @@ import {
import { makeMigrationCall } from '../DataActions';
import toast from 'react-hot-toast/headless';
import { getNotificationDetails } from '../../Common/Notification';
import { getTableConfiguration } from '../TableBrowseRows/utils';
import {
getTableConfiguration,
getTableCustomization,
} from '../TableBrowseRows/utils';
const I_RESET = 'InsertItem/I_RESET';
const I_ONGOING_REQ = 'InsertItem/I_ONGOING_REQ';
@ -92,6 +95,7 @@ const insertItem = (tableName, colValues, isMigration = false) => {
const tableDef = { name: tableName, schema: currentSchema };
const sources = metadata.metadataObject?.sources;
const tableConfiguration = getTableConfiguration(tables, sources);
const tableCustomization = getTableCustomization(tables, sources);
const currentTableInfo = findTable(allSchemas, tableDef);
if (!currentTableInfo) return;
const columns = currentTableInfo.columns;
@ -157,6 +161,7 @@ const insertItem = (tableName, colValues, isMigration = false) => {
source: currentDataSource,
tableDef,
tableConfiguration,
dataSourceCustomization: tableCustomization,
insertObject,
returning,
});
@ -206,6 +211,7 @@ const insertItem = (tableName, colValues, isMigration = false) => {
const { affectedRows, returnedFields } = processInsertData(
data,
tableConfiguration,
tableCustomization,
{
currentSchema,
currentTable: tableName,

View File

@ -0,0 +1,110 @@
import { getQueryWithNamespace, getFullQueryNameBase } from './graphqlUtil';
import { formatSdl } from 'format-graphql';
describe('getQueryWithNamespace', () => {
it('returns a query with namespace', () => {
const result = getQueryWithNamespace({
namespace: 'namespace',
queryName: 'query Rows',
innerQuery: 'Album { id }',
});
const expected = formatSdl(`query Rows
{
namespace {
Album { id }
}
}`);
expect(result).toEqual(expected);
});
it('returns a query without namespace', () => {
const result = getQueryWithNamespace({
namespace: '',
queryName: 'query Rows',
innerQuery: 'Album { id }',
});
const expected = formatSdl(`query Rows
{
Album { id }
}`);
expect(result).toEqual(expected);
});
});
describe('getFullQueryNameBase', () => {
it.each`
custom_name | custom_root_fields | namespace | prefix | suffix | operation | expected
${''} | ${{}} | ${''} | ${''} | ${''} | ${'select'} | ${'Album'}
${'alb'} | ${{}} | ${''} | ${''} | ${''} | ${'select'} | ${'alb'}
${''} | ${{}} | ${'ns'} | ${''} | ${''} | ${'select'} | ${'Album'}
${''} | ${{}} | ${''} | ${'pre_'} | ${''} | ${'select'} | ${'pre_Album'}
${''} | ${{}} | ${''} | ${''} | ${'_suf'} | ${'select'} | ${'Album_suf'}
${''} | ${{}} | ${'ns'} | ${'pre_'} | ${'_suf'} | ${'select'} | ${'pre_Album_suf'}
${''} | ${{ select: 'my_select' }} | ${'ns'} | ${'pre_'} | ${'_suf'} | ${'select'} | ${'pre_my_select_suf'}
${'alb'} | ${{ select: 'my_select' }} | ${'ns'} | ${'pre_'} | ${'_suf'} | ${'select'} | ${'pre_my_select_suf'}
${''} | ${{}} | ${''} | ${''} | ${''} | ${'update'} | ${'update_Album'}
${'alb'} | ${{}} | ${''} | ${''} | ${''} | ${'update'} | ${'update_alb'}
${''} | ${{}} | ${'ns'} | ${''} | ${''} | ${'update'} | ${'update_Album'}
${''} | ${{}} | ${''} | ${'pre_'} | ${''} | ${'update'} | ${'pre_update_Album'}
${''} | ${{}} | ${''} | ${''} | ${'_suf'} | ${'update'} | ${'update_Album_suf'}
${''} | ${{}} | ${'ns'} | ${'pre_'} | ${'_suf'} | ${'update'} | ${'pre_update_Album_suf'}
${''} | ${{ update: 'my_update' }} | ${'ns'} | ${'pre_'} | ${'_suf'} | ${'update'} | ${'pre_my_update_suf'}
${'alb'} | ${{ update: 'my_update' }} | ${'ns'} | ${'pre_'} | ${'_suf'} | ${'select'} | ${'pre_alb_suf'}
${''} | ${{}} | ${''} | ${''} | ${''} | ${'select_aggregate'} | ${'Album_aggregate'}
${'alb'} | ${{}} | ${''} | ${''} | ${''} | ${'select_aggregate'} | ${'alb_aggregate'}
${''} | ${{}} | ${'ns'} | ${''} | ${''} | ${'select_aggregate'} | ${'Album_aggregate'}
${''} | ${{}} | ${''} | ${'pre_'} | ${''} | ${'select_aggregate'} | ${'pre_Album_aggregate'}
${''} | ${{}} | ${''} | ${''} | ${'_suf'} | ${'select_aggregate'} | ${'Album_aggregate_suf'}
${''} | ${{}} | ${'ns'} | ${'pre_'} | ${'_suf'} | ${'select_aggregate'} | ${'pre_Album_aggregate_suf'}
${''} | ${{ select_aggregate: 'my_select_aggregate' }} | ${'ns'} | ${'pre_'} | ${'_suf'} | ${'select_aggregate'} | ${'pre_my_select_aggregate_suf'}
${'alb'} | ${{ select_aggregate: 'my_select_aggregate' }} | ${'ns'} | ${'pre_'} | ${'_suf'} | ${'select_aggregate'} | ${'pre_my_select_aggregate_suf'}
${''} | ${{}} | ${''} | ${''} | ${''} | ${'insert'} | ${'insert_Album'}
${'alb'} | ${{}} | ${''} | ${''} | ${''} | ${'insert'} | ${'insert_alb'}
${''} | ${{}} | ${'ns'} | ${''} | ${''} | ${'insert'} | ${'insert_Album'}
${''} | ${{}} | ${''} | ${'pre_'} | ${''} | ${'insert'} | ${'pre_insert_Album'}
${''} | ${{}} | ${''} | ${''} | ${'_suf'} | ${'insert'} | ${'insert_Album_suf'}
${''} | ${{}} | ${'ns'} | ${'pre_'} | ${'_suf'} | ${'insert'} | ${'pre_insert_Album_suf'}
${''} | ${{ insert: 'my_insert' }} | ${''} | ${''} | ${''} | ${'insert'} | ${'my_insert'}
${'alb'} | ${{ insert: 'my_insert' }} | ${'ns'} | ${'pre_'} | ${'_suf'} | ${'insert'} | ${'pre_my_insert_suf'}
${''} | ${{}} | ${''} | ${''} | ${''} | ${'delete'} | ${'delete_Album'}
${'alb'} | ${{}} | ${''} | ${''} | ${''} | ${'delete'} | ${'delete_alb'}
${''} | ${{}} | ${'ns'} | ${''} | ${''} | ${'delete'} | ${'delete_Album'}
${''} | ${{}} | ${''} | ${'pre_'} | ${''} | ${'delete'} | ${'pre_delete_Album'}
${''} | ${{}} | ${''} | ${''} | ${'_suf'} | ${'delete'} | ${'delete_Album_suf'}
${''} | ${{}} | ${'ns'} | ${'pre_'} | ${'_suf'} | ${'delete'} | ${'pre_delete_Album_suf'}
${''} | ${{ delete: 'my_delete' }} | ${''} | ${''} | ${''} | ${'delete'} | ${'my_delete'}
${'alb'} | ${{ delete: 'my_delete' }} | ${''} | ${''} | ${''} | ${'delete'} | ${'my_delete'}
`(
'given custom name: "$custom_name", custom_root_fields: $custom_root_fields, namespace: "$namespace", prefix: "$prefix", suffix: "$suffix", operation: "$operation", returns "$expected"',
({
custom_name,
custom_root_fields,
namespace,
prefix,
suffix,
operation,
expected,
}) => {
const result = getFullQueryNameBase('public')({
tableName: 'Album',
schema: 'public',
tableConfiguration: {
custom_name,
custom_root_fields,
},
dataSourceCustomization: {
root_fields: {
namespace,
prefix,
suffix,
},
},
operation,
});
expect(result).toEqual(expected);
}
);
});

View File

@ -1,3 +1,4 @@
import { formatSdl } from 'format-graphql';
import { CustomRootFields, TableConfig } from './../../metadata/types';
import {
OrderBy,
@ -5,6 +6,8 @@ import {
} from '../../components/Common/utils/v1QueryUtils';
import { ReduxState } from '../../types';
import { BaseTableColumn, Relationship, Table } from '../types';
import { SourceCustomization } from '../../features/hasura-metadata-types';
import { getQueryName } from './utils';
type Tables = ReduxState['tables'];
@ -26,6 +29,7 @@ interface GetFullQueryName {
tableName: string;
schema: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
defaultSchema?: string;
operation: keyof Omit<
CustomRootFields,
@ -50,18 +54,31 @@ export const getColQuery = (
cols: (string | { name: string; columns: string[] })[],
limit: number,
relationships: Relationship[],
tableConfiguration: TableConfig
tableConfiguration: TableConfig,
dataSourceCustomization: SourceCustomization
): string[] => {
return cols.map(c => {
return cols.map(column => {
const columnConfig = tableConfiguration?.column_config ?? {};
if (typeof c === 'string') return columnConfig[c]?.custom_name ?? c;
const rel = relationships.find((r: any) => r.rel_name === c.name);
return `${columnConfig[c.name]?.custom_name ?? c.name} ${
if (typeof column === 'string')
return columnConfig[column]?.custom_name ?? column;
const queryName = getQueryName({
column,
tableConfiguration,
dataSourceCustomization,
});
const rel = relationships.find((r: any) => r.rel_name === column.name);
return `${queryName} ${
rel?.rel_type === 'array' ? `(limit: ${limit})` : ''
} {
${getColQuery(c.columns, limit, relationships, tableConfiguration).join(
'\n'
)} }`;
${getColQuery(
column.columns,
limit,
relationships,
tableConfiguration,
dataSourceCustomization
).join('\n')} }`;
});
};
@ -175,20 +192,53 @@ export const getFullQueryNameBase =
tableName,
schema,
tableConfiguration,
dataSourceCustomization,
operation,
}: GetFullQueryName): string => {
const prefix = dataSourceCustomization?.root_fields?.prefix ?? '';
const suffix = dataSourceCustomization?.root_fields?.suffix ?? '';
const customRootFields = tableConfiguration?.custom_root_fields ?? {};
const customRootField = customRootFields[operation];
if (typeof customRootField === 'string') return customRootField;
else if (customRootField?.name) return customRootField.name;
const withUpdate = operation === 'update' ? 'update_' : '';
const withUpdate =
operation === 'update' && !customRootFields?.update ? 'update_' : '';
const withSchema =
schema === defaultSchema || tableConfiguration?.custom_name
? ''
: `${schema}_`;
const withAgg = operation === 'select_aggregate' ? `_aggregate` : '';
const withDelete = operation === 'delete' ? 'delete_' : '';
const withInsert = operation === 'insert' ? 'insert_' : '';
const trackedTableName = tableConfiguration?.custom_name || tableName;
return `${withDelete}${withUpdate}${withInsert}${withSchema}${trackedTableName}${withAgg}`;
const withAgg =
operation === 'select_aggregate' && !customRootFields?.select_aggregate
? `_aggregate`
: '';
const withDelete =
operation === 'delete' && !customRootFields?.delete ? 'delete_' : '';
const withInsert =
operation === 'insert' && !customRootFields?.insert ? 'insert_' : '';
const trackedTableName =
customRootField || tableConfiguration?.custom_name || tableName;
return `${prefix}${withDelete}${withUpdate}${withInsert}${withSchema}${trackedTableName}${withAgg}${suffix}`;
};
type GetQueryWithNamespaceArgs = {
queryName: string;
namespace: string;
innerQuery: string;
};
export const getQueryWithNamespace = ({
queryName,
namespace,
innerQuery,
}: GetQueryWithNamespaceArgs) => {
return formatSdl(`${queryName}
{
${namespace ? `${namespace} {` : ''}
${innerQuery}
${namespace ? `}` : ''}
}`);
};

View File

@ -0,0 +1,89 @@
import { getQueryName } from './utils';
describe('getQueryName', () => {
describe('when no customizations', () => {
it('returns the query name', () => {
const result = getQueryName({
column: {
name: 'name',
columns: ['name'],
},
tableConfiguration: {
custom_name: '',
},
dataSourceCustomization: {},
});
expect(result).toEqual('name');
});
});
describe('when table configuration is present', () => {
it('returns the query name', () => {
const result = getQueryName({
column: {
name: 'name',
columns: ['name'],
},
tableConfiguration: {
custom_name: 'my_name',
column_config: {
name: {
custom_name: 'my_custom_name',
},
},
},
dataSourceCustomization: {},
});
expect(result).toEqual('my_custom_name');
});
});
describe('when data source customization are present', () => {
it('returns the query name', () => {
const result = getQueryName({
column: {
name: 'name',
columns: ['name'],
},
tableConfiguration: {
custom_name: '',
},
dataSourceCustomization: {
root_fields: {
prefix: 'pre_',
suffix: '_suf',
namespace: 'ns',
},
},
});
expect(result).toEqual('pre_name_suf');
});
});
describe('when table configuration and data source customization are present', () => {
it('returns the query name', () => {
const result = getQueryName({
column: {
name: 'name',
columns: ['name'],
},
tableConfiguration: {
custom_name: 'my_name',
column_config: {
name: {
custom_name: 'my_custom_name',
},
},
},
dataSourceCustomization: {
root_fields: {
prefix: 'pre_',
suffix: '_suf',
namespace: 'ns',
},
},
});
expect(result).toEqual('pre_my_custom_name_suf');
});
});
});

View File

@ -0,0 +1,37 @@
import { SourceCustomization } from '../../features/hasura-metadata-types';
import { TableConfig } from '../../metadata/types';
export type Column = { name: string; columns: string[] };
export type QueryNameArgs = {
column: Column;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
};
export const getQueryName = ({
column,
tableConfiguration,
dataSourceCustomization,
}: QueryNameArgs) => {
const columnConfig = tableConfiguration?.column_config ?? {};
const prefix = dataSourceCustomization.root_fields?.prefix ?? '';
const suffix = dataSourceCustomization.root_fields?.suffix ?? '';
const columnName = column.name;
if (columnConfig[columnName]?.custom_name) {
return `${prefix}${columnConfig[columnName].custom_name}${suffix}`;
}
if (columnConfig[columnName]?.custom_name) {
return `${prefix}${columnConfig[columnName].custom_name}${suffix}`;
}
if (tableConfiguration?.custom_root_fields?.select) {
return `${prefix}${tableConfiguration.custom_root_fields.select}${suffix}`;
}
return `${prefix}${columnName}${suffix}`;
};

View File

@ -8,6 +8,8 @@ import { TableConfig } from '../../../metadata/types';
import { ReduxState } from '../../../types';
import { Relationship } from '../../types';
import { isEmpty } from '../../../components/Common/utils/jsUtils';
import { SourceCustomization } from '../../../features/hasura-metadata-types';
import { getQueryName } from '../../common/utils';
type Tables = ReduxState['tables'];
@ -64,18 +66,31 @@ const getColQuery = (
cols: (string | { name: string; columns: string[] })[],
limit: number,
relationships: Relationship[],
tableConfiguration: TableConfig
tableConfiguration: TableConfig,
dataSourceCustomization: SourceCustomization
): string[] => {
return cols.map(c => {
return cols.map(column => {
const columnConfig = tableConfiguration?.column_config ?? {};
if (typeof c === 'string') return columnConfig[c]?.custom_name ?? c;
const rel = relationships.find((r: any) => r.rel_name === c.name);
return `${columnConfig[c.name]?.custom_name ?? c.name} ${
if (typeof column === 'string')
return columnConfig[column]?.custom_name ?? column;
const queryName = getQueryName({
column,
tableConfiguration,
dataSourceCustomization,
});
const rel = relationships.find((r: any) => r.rel_name === column.name);
return `${queryName} ${
rel?.rel_type === 'array' ? `(limit: ${limit})` : ''
} {
${getColQuery(c.columns, limit, relationships, tableConfiguration).join(
'\n'
)} }`;
${getColQuery(
column.columns,
limit,
relationships,
tableConfiguration,
dataSourceCustomization
).join('\n')} }`;
});
};
@ -83,10 +98,12 @@ export const getTableRowRequestBody = ({
tables,
isExport,
tableConfiguration,
dataSourceCustomization,
}: {
tables: Tables;
isExport?: boolean;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => {
const {
currentTable: originalTable,
@ -99,12 +116,14 @@ export const getTableRowRequestBody = ({
tableName,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select',
});
const aggregateName = getFullQueryName({
tableName,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select_aggregate',
});
const queryBody = ({ clauses, relationshipInfo }: QueryBody) => {
@ -114,7 +133,8 @@ export const getTableRowRequestBody = ({
view.query.columns,
view.curFilter.limit,
relationshipInfo,
tableConfiguration
tableConfiguration,
dataSourceCustomization
).join('\n')}
}
${aggregateName} {
@ -147,9 +167,15 @@ const processTableRowData = (
originalTable: string;
currentSchema: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}
) => {
const { originalTable, currentSchema, tableConfiguration } = config!;
const {
originalTable,
currentSchema,
tableConfiguration,
dataSourceCustomization,
} = config!;
const reversedCustomColumns = Object.entries(
tableConfiguration?.column_config ?? {}
@ -163,6 +189,7 @@ const processTableRowData = (
tableName,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select',
});
const results = data?.data[queryName];
@ -192,9 +219,11 @@ export const generateTableRowRequest = () => ({
export const getRowsCountRequestBody = ({
tables,
tableConfiguration,
dataSourceCustomization,
}: {
tables: Tables;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => {
const {
currentTable: originalTable,
@ -207,6 +236,7 @@ export const getRowsCountRequestBody = ({
tableName: originalTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select_aggregate',
});
return `query TableCount {
@ -239,11 +269,13 @@ const processCount = (c: {
currentSchema: string;
originalTable: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}): number => {
const key = getFullQueryName({
tableName: c.originalTable,
schema: c.currentSchema,
tableConfiguration: c.tableConfiguration,
dataSourceCustomization: c.dataSourceCustomization,
operation: 'select_aggregate',
});
return c.data?.data?.[key]?.aggregate?.count;

View File

@ -19,9 +19,11 @@ import {
getColQuery,
getFullQueryNameBase,
getGraphQLQueryBase,
getQueryWithNamespace,
} from '../../common';
import { WhereClause } from '../../../components/Common/utils/v1QueryUtils';
import { replaceAllStringOccurrences } from '../../common/index';
import { SourceCustomization } from '../../../features/hasura-metadata-types';
type Tables = ReduxState['tables'];
@ -107,9 +109,11 @@ const getFullQueryName = getFullQueryNameBase('public');
export const getRowsCountRequestBody = ({
tables,
tableConfiguration,
dataSourceCustomization,
}: {
tables: Tables;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => {
const {
currentTable: originalTable,
@ -122,15 +126,22 @@ export const getRowsCountRequestBody = ({
tableName: originalTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select_aggregate',
});
return `query TableCount {
${queryName} ${clauses && `(${clauses})`} {
aggregate {
count
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
return getQueryWithNamespace({
queryName: 'query TableCount',
namespace: namespace,
innerQuery: `
${queryName} ${clauses && `(${clauses})`} {
aggregate {
count
}
}
}
}`;
`,
});
};
return {
@ -154,11 +165,13 @@ const processCount = (c: {
currentSchema: string;
originalTable: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}): number => {
const key = getFullQueryName({
tableName: c.originalTable,
schema: c.currentSchema,
tableConfiguration: c.tableConfiguration,
dataSourceCustomization: c.dataSourceCustomization,
operation: 'select_aggregate',
});
return c.data?.data[key]?.aggregate?.count;
@ -174,10 +187,12 @@ export const getTableRowRequestBody = ({
tables,
isExport,
tableConfiguration,
dataSourceCustomization,
}: {
tables: Tables;
isExport?: boolean;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => {
const {
currentTable: originalTable,
@ -189,30 +204,41 @@ export const getTableRowRequestBody = ({
tableName: originalTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select',
});
const aggregateName = getFullQueryName({
tableName: originalTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select_aggregate',
});
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const queryBody = ({ clauses, relationshipInfo }: QueryBody) => {
return `query TableRows {
${queryName} ${clauses && `(${clauses})`} {
return getQueryWithNamespace({
queryName: 'query TableRows',
namespace: namespace,
innerQuery: `
${queryName} ${clauses && `(${clauses})`}
{
${getColQuery(
view.query.columns,
view.curFilter.limit,
relationshipInfo,
tableConfiguration
tableConfiguration,
dataSourceCustomization
).join('\n')}
}
${aggregateName} {
aggregate {
count
}
}
}`;
${aggregateName}
{
aggregate {
count
}
}
`,
});
};
return {
@ -237,10 +263,16 @@ export const processTableRowData = (
originalTable: string;
currentSchema: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}
) => {
try {
const { originalTable, currentSchema, tableConfiguration } = config!;
const {
originalTable,
currentSchema,
tableConfiguration,
dataSourceCustomization,
} = config!;
const reversedCustomColumns = Object.entries(
tableConfiguration?.column_config ?? {}
@ -253,9 +285,16 @@ export const processTableRowData = (
tableName: originalTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select',
});
const results = data?.data[queryName];
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const hasNamespace = !!namespace;
const results = hasNamespace
? data?.data[namespace][queryName]
: data?.data[queryName];
const rows = isEmpty(reversedCustomColumns)
? results
@ -273,6 +312,7 @@ export const processTableRowData = (
tableName: originalTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select_aggregate',
});
const estimatedCount =
@ -293,7 +333,7 @@ const getInsertRequestBody = (
data: Parameters<generateInsertRequestType['getInsertRequestBody']>[0]
): ReturnType<generateInsertRequestType['getInsertRequestBody']> => {
const { name: tableName, schema } = data.tableDef;
const { tableConfiguration } = data;
const { tableConfiguration, dataSourceCustomization } = data;
const columnConfig = tableConfiguration?.column_config ?? {};
const processedData: Record<string, any> = {};
@ -316,18 +356,21 @@ const getInsertRequestBody = (
tableName,
schema,
tableConfiguration,
dataSourceCustomization,
operation: 'insert',
});
const query = `
mutation InsertRow {
${queryName}(objects: { ${values} }){
returning {
${returning}
}
}
}
`;
const query = getQueryWithNamespace({
queryName: 'mutation InsertRow',
namespace: dataSourceCustomization?.root_fields?.namespace ?? '',
innerQuery: `
${queryName}(objects: { ${values} }){
returning {
${returning}
}
}
`,
});
return {
query,
@ -342,20 +385,31 @@ type processInsertDataParameter = Parameters<
const processInsertData = (
result: processInsertDataParameter[0],
tableConfiguration: TableConfig,
config: processInsertDataParameter[2]
dataSourceCustomization: SourceCustomization,
config: processInsertDataParameter[3]
) => {
const { currentTable, currentSchema } = config!;
const index = getFullQueryName({
tableName: currentTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'insert',
});
const returnedFields = (
result as {
data: Record<string, Record<string, any>>;
}
)?.data?.[index]?.returning;
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const hasNamespace = !!namespace;
const _result = result as {
data: Record<string, Record<string, any>>;
};
const data = hasNamespace
? _result?.data[namespace][index]
: _result?.data[index];
const returnedFields = data?.returning;
return {
affectedRows: 1,
returnedFields:
@ -410,10 +464,11 @@ const getEditRowRequestBody = (data: {
source: string;
tableDef: QualifiedTable;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
set: Record<string, any>;
where: Record<string, any>;
}) => {
const { tableConfiguration } = data;
const { tableConfiguration, dataSourceCustomization } = data;
const columnConfig = tableConfiguration?.column_config || {};
const whereClause = Object.entries(data.where)
.map(
@ -437,14 +492,21 @@ const getEditRowRequestBody = (data: {
tableName,
schema,
tableConfiguration,
dataSourceCustomization,
operation: 'update',
});
const query = `mutation {
${operationName}(where: {${whereClause}}, _set: {${setClause}}) {
affected_rows
}
}`;
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const query = getQueryWithNamespace({
queryName: 'mutation EditRow',
namespace,
innerQuery: `
${operationName}(where: {${whereClause}}, _set: {${setClause}}) {
affected_rows
}
`,
});
return {
query,
variables: null,
@ -455,19 +517,30 @@ const processEditData = ({
data,
tableDef,
tableConfiguration,
dataSourceCustomization,
}: {
tableDef: QualifiedTable;
data: any;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}): number => {
const { name: tableName, schema } = tableDef;
const operationName = getFullQueryName({
tableName,
schema,
tableConfiguration,
dataSourceCustomization,
operation: 'update',
});
return data?.data[operationName].affected_rows;
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const hasNamespace = !!namespace;
const results = hasNamespace
? data?.data[namespace][operationName]
: data?.data[operationName];
return results.affected_rows;
};
export const generateEditRowRequest = () => ({
@ -482,12 +555,14 @@ const getDeleteRowRequestBody = ({
schemaName,
columnInfo,
tableConfiguration,
dataSourceCustomization,
}: {
pkClause: WhereClause;
tableName: string;
schemaName: string;
columnInfo: BaseTableColumn[];
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => {
const columnConfig = tableConfiguration?.column_config || {};
@ -504,25 +579,46 @@ const getDeleteRowRequestBody = ({
tableName,
schema: schemaName,
tableConfiguration,
dataSourceCustomization,
operation: 'delete',
});
const query = `mutation DeleteRows {
delete_row: ${identifier}(where: {${args}}) {
affected_rows
}
}`;
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const query = getQueryWithNamespace({
queryName: 'mutation DeleteRows',
namespace,
innerQuery: `
delete_row: ${identifier}(where: {${args}}) {
affected_rows
}
`,
});
return {
query,
variables: null,
};
};
const processDeleteRowData = (data: Record<string, any>) => {
const processDeleteRowData = (
data: Record<string, any>,
config: {
dataSourceCustomization: SourceCustomization;
}
) => {
try {
if (data.errors) throw new Error(data.errors[0].message);
if (data?.data?.delete_row?.affected_rows) {
return data?.data?.delete_row?.affected_rows;
}
const namespace =
config.dataSourceCustomization?.root_fields?.namespace ?? '';
const hasNamespace = !!namespace;
const results = hasNamespace
? data?.data[namespace]?.delete_row
: data?.data?.delete_row;
if (results?.affected_rows) return results?.affected_rows;
throw new Error('Invalid response');
} catch (err) {
if (isConsoleError(err)) {
@ -543,18 +639,21 @@ const getBulkDeleteRowRequestBody = ({
schemaName,
columnInfo,
tableConfiguration,
dataSourceCustomization,
}: {
pkClauses: WhereClause[];
tableName: string;
schemaName: string;
columnInfo: BaseTableColumn[];
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => {
const columnConfig = tableConfiguration?.column_config || {};
const identifier = getFullQueryName({
tableName,
schema: schemaName,
tableConfiguration,
dataSourceCustomization,
operation: 'delete',
});
const topLevelFields = pkClauses.map((pkClause, i) => {
@ -570,25 +669,43 @@ const getBulkDeleteRowRequestBody = ({
return `delete_row_${i}: ${identifier}(where: {${args}}) { affected_rows }`;
});
const query = `mutation MyMutation {
${topLevelFields.join('\n')}
}`;
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const query = getQueryWithNamespace({
queryName: 'mutation BulkDeleteRows',
namespace,
innerQuery: `
${topLevelFields.join('\n')}
`,
});
return {
query,
variables: null,
};
};
const processBulkDeleteRowData = (data: Record<string, any>) => {
const processBulkDeleteRowData = (
data: Record<string, any>,
config: {
dataSourceCustomization: SourceCustomization;
}
) => {
try {
if (data.errors) {
throw new Error(data.errors[0].message);
}
if (data.data) {
const res = Object.keys(data.data)
.filter(key => data.data[key])
.map(key => data.data[key].affected_rows)
const namespace =
config.dataSourceCustomization?.root_fields?.namespace ?? '';
const hasNamespace = !!namespace;
const results = hasNamespace ? data?.data[namespace] : data?.data;
const res = Object.keys(results)
.filter(key => results[key])
.map(key => results[key].affected_rows)
.reduce((a, b) => a + b, 0);
return res;
}

View File

@ -19,9 +19,11 @@ import {
getColQuery,
getFullQueryNameBase,
getGraphQLQueryBase,
getQueryWithNamespace,
} from '../../common';
import { WhereClause } from '../../../components/Common/utils/v1QueryUtils';
import { replaceAllStringOccurrences } from '../../common/index';
import { SourceCustomization } from '../../../features/hasura-metadata-types';
type Tables = ReduxState['tables'];
@ -108,9 +110,11 @@ const getFullQueryName = getFullQueryNameBase('public');
export const getRowsCountRequestBody = ({
tables,
tableConfiguration,
dataSourceCustomization,
}: {
tables: Tables;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => {
const {
currentTable: originalTable,
@ -123,15 +127,22 @@ export const getRowsCountRequestBody = ({
tableName: originalTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select_aggregate',
});
return `query TableCount {
${queryName} ${clauses && `(${clauses})`} {
aggregate {
count
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
return getQueryWithNamespace({
queryName: 'query TableCount',
namespace: namespace,
innerQuery: `
${queryName} ${clauses && `(${clauses})`} {
aggregate {
count
}
}
}
}`;
`,
});
};
return {
@ -155,11 +166,13 @@ const processCount = (c: {
currentSchema: string;
originalTable: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}): number => {
const key = getFullQueryName({
tableName: c.originalTable,
schema: c.currentSchema,
tableConfiguration: c.tableConfiguration,
dataSourceCustomization: c.dataSourceCustomization,
operation: 'select_aggregate',
});
return c.data?.data[key]?.aggregate?.count;
@ -175,10 +188,12 @@ export const getTableRowRequestBody = ({
tables,
isExport,
tableConfiguration,
dataSourceCustomization,
}: {
tables: Tables;
isExport?: boolean;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => {
const {
currentTable: originalTable,
@ -190,30 +205,41 @@ export const getTableRowRequestBody = ({
tableName: originalTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select',
});
const aggregateName = getFullQueryName({
tableName: originalTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select_aggregate',
});
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const queryBody = ({ clauses, relationshipInfo }: QueryBody) => {
return `query TableRows {
${queryName} ${clauses && `(${clauses})`} {
return getQueryWithNamespace({
queryName: 'query TableRows',
namespace: namespace,
innerQuery: `
${queryName} ${clauses && `(${clauses})`}
{
${getColQuery(
view.query.columns,
view.curFilter.limit,
relationshipInfo,
tableConfiguration
tableConfiguration,
dataSourceCustomization
).join('\n')}
}
${aggregateName} {
aggregate {
count
}
}
}`;
${aggregateName}
{
aggregate {
count
}
}
`,
});
};
return {
@ -238,10 +264,16 @@ export const processTableRowData = (
originalTable: string;
currentSchema: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}
) => {
try {
const { originalTable, currentSchema, tableConfiguration } = config!;
const {
originalTable,
currentSchema,
tableConfiguration,
dataSourceCustomization,
} = config!;
const reversedCustomColumns = Object.entries(
tableConfiguration?.column_config ?? {}
@ -254,9 +286,16 @@ export const processTableRowData = (
tableName: originalTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select',
});
const results = data?.data[queryName];
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const hasNamespace = !!namespace;
const results = hasNamespace
? data?.data[namespace][queryName]
: data?.data[queryName];
const rows = isEmpty(reversedCustomColumns)
? results
@ -274,6 +313,7 @@ export const processTableRowData = (
tableName: originalTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select_aggregate',
});
const estimatedCount =
@ -294,7 +334,7 @@ const getInsertRequestBody = (
data: Parameters<generateInsertRequestType['getInsertRequestBody']>[0]
): ReturnType<generateInsertRequestType['getInsertRequestBody']> => {
const { name: tableName, schema } = data.tableDef;
const { tableConfiguration } = data;
const { tableConfiguration, dataSourceCustomization } = data;
const columnConfig = tableConfiguration?.column_config ?? {};
const processedData: Record<string, any> = {};
@ -317,18 +357,21 @@ const getInsertRequestBody = (
tableName,
schema,
tableConfiguration,
dataSourceCustomization,
operation: 'insert',
});
const query = `
mutation InsertRow {
${queryName}(objects: { ${values} }){
returning {
${returning}
}
}
}
`;
const query = getQueryWithNamespace({
queryName: 'mutation InsertRow',
namespace: dataSourceCustomization?.root_fields?.namespace ?? '',
innerQuery: `
${queryName}(objects: { ${values} }){
returning {
${returning}
}
}
`,
});
return {
query,
@ -343,20 +386,30 @@ type processInsertDataParameter = Parameters<
const processInsertData = (
result: processInsertDataParameter[0],
tableConfiguration: TableConfig,
config: processInsertDataParameter[2]
dataSourceCustomization: SourceCustomization,
config: processInsertDataParameter[3]
) => {
const { currentTable, currentSchema } = config!;
const index = getFullQueryName({
tableName: currentTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'insert',
});
const returnedFields = (
result as {
data: Record<string, Record<string, any>>;
}
)?.data?.[index]?.returning;
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const hasNamespace = !!namespace;
const _result = result as {
data: Record<string, Record<string, any>>;
};
const data = hasNamespace
? _result?.data[namespace][index]
: _result?.data[index];
const returnedFields = data?.returning;
return {
affectedRows: 1,
returnedFields:
@ -411,10 +464,11 @@ const getEditRowRequestBody = (data: {
source: string;
tableDef: QualifiedTable;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
set: Record<string, any>;
where: Record<string, any>;
}) => {
const { tableConfiguration } = data;
const { tableConfiguration, dataSourceCustomization } = data;
const columnConfig = tableConfiguration?.column_config || {};
const whereClause = Object.entries(data.where)
.map(
@ -438,14 +492,21 @@ const getEditRowRequestBody = (data: {
tableName,
schema,
tableConfiguration,
dataSourceCustomization,
operation: 'update',
});
const query = `mutation {
${operationName}(where: {${whereClause}}, _set: {${setClause}}) {
affected_rows
}
}`;
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const query = getQueryWithNamespace({
queryName: 'mutation EditRow',
namespace,
innerQuery: `
${operationName}(where: {${whereClause}}, _set: {${setClause}}) {
affected_rows
}
`,
});
return {
query,
variables: null,
@ -456,19 +517,30 @@ const processEditData = ({
data,
tableDef,
tableConfiguration,
dataSourceCustomization,
}: {
tableDef: QualifiedTable;
data: any;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}): number => {
const { name: tableName, schema } = tableDef;
const operationName = getFullQueryName({
tableName,
schema,
tableConfiguration,
dataSourceCustomization,
operation: 'update',
});
return data?.data[operationName].affected_rows;
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const hasNamespace = !!namespace;
const results = hasNamespace
? data?.data[namespace][operationName]
: data?.data[operationName];
return results.affected_rows;
};
export const generateEditRowRequest = () => ({
@ -483,12 +555,14 @@ const getDeleteRowRequestBody = ({
schemaName,
columnInfo,
tableConfiguration,
dataSourceCustomization,
}: {
pkClause: WhereClause;
tableName: string;
schemaName: string;
columnInfo: BaseTableColumn[];
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => {
const columnConfig = tableConfiguration?.column_config || {};
@ -505,24 +579,45 @@ const getDeleteRowRequestBody = ({
tableName,
schema: schemaName,
tableConfiguration,
dataSourceCustomization,
operation: 'delete',
});
const query = `mutation DeleteRows {
delete_row: ${identifier}(where: {${args}}) {
affected_rows
}
}`;
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const query = getQueryWithNamespace({
queryName: 'mutation DeleteRows',
namespace,
innerQuery: `
delete_row: ${identifier}(where: {${args}}) {
affected_rows
}
`,
});
return {
query,
variables: null,
};
};
const processDeleteRowData = (data: Record<string, any>) => {
const processDeleteRowData = (
data: Record<string, any>,
config: {
dataSourceCustomization: SourceCustomization;
}
) => {
try {
if (data.errors) throw new Error(data.errors[0].message);
if (data?.data?.delete_row?.affected_rows)
return data?.data?.delete_row?.affected_rows;
const namespace =
config.dataSourceCustomization?.root_fields?.namespace ?? '';
const hasNamespace = !!namespace;
const results = hasNamespace
? data?.data[namespace]?.delete_row
: data?.data?.delete_row;
if (results?.affected_rows) return results?.affected_rows;
throw new Error('Invalid response');
} catch (err) {
if (isConsoleError(err)) {
@ -543,18 +638,21 @@ const getBulkDeleteRowRequestBody = ({
schemaName,
columnInfo,
tableConfiguration,
dataSourceCustomization,
}: {
pkClauses: WhereClause[];
tableName: string;
schemaName: string;
columnInfo: BaseTableColumn[];
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => {
const columnConfig = tableConfiguration?.column_config || {};
const identifier = getFullQueryName({
tableName,
schema: schemaName,
tableConfiguration,
dataSourceCustomization,
operation: 'delete',
});
const topLevelFields = pkClauses.map((pkClause, i) => {
@ -570,23 +668,41 @@ const getBulkDeleteRowRequestBody = ({
return `delete_row_${i}: ${identifier}(where: {${args}}) { affected_rows }`;
});
const query = `mutation MyMutation {
${topLevelFields.join('\n')}
}`;
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const query = getQueryWithNamespace({
queryName: 'mutation BulkDeleteRows',
namespace,
innerQuery: `
${topLevelFields.join('\n')}
`,
});
return {
query,
variables: null,
};
};
const processBulkDeleteRowData = (data: Record<string, any>) => {
const processBulkDeleteRowData = (
data: Record<string, any>,
config: {
dataSourceCustomization: SourceCustomization;
}
) => {
try {
if (data.errors) throw new Error(data.errors[0].message);
if (data.data) {
const res = Object.keys(data.data)
.filter(key => data.data[key])
.map(key => data.data[key].affected_rows)
const namespace =
config.dataSourceCustomization?.root_fields?.namespace ?? '';
const hasNamespace = !!namespace;
const results = hasNamespace ? data?.data[namespace] : data?.data;
const res = Object.keys(results)
.filter(key => results[key])
.map(key => results[key].affected_rows)
.reduce((a, b) => a + b, 0);
return res;
}

View File

@ -13,15 +13,15 @@ exports[`mssql datasource tests getAddUniqueConstraintSql should generate SQL qu
`;
exports[`mssql datasource tests getAlterColumnCommentSql should generate SQL for modifying column comment 1`] = `
"IF EXISTS (SELECT NULL FROM SYS.EXTENDED_PROPERTIES WHERE [major_id] = OBJECT_ID('users') AND [name] = N'column_comment_public_users_id' AND [minor_id] = (SELECT [column_id] FROM SYS.COLUMNS WHERE [name] = 'id' AND [object_id] = OBJECT_ID('users')))
EXECUTE sp_dropextendedproperty
@name = N'column_comment_public_users_id',
"IF EXISTS (SELECT NULL FROM SYS.EXTENDED_PROPERTIES WHERE [major_id] = OBJECT_ID('users') AND [name] = N'column_comment_public_users_id' AND [minor_id] = (SELECT [column_id] FROM SYS.COLUMNS WHERE [name] = 'id' AND [object_id] = OBJECT_ID('users')))
EXECUTE sp_dropextendedproperty
@name = N'column_comment_public_users_id',
@level0type = N'SCHEMA', @level0name = 'public'
,@level1type = N'TABLE', @level1name = 'users',@level2type = N'COLUMN', @level2name = 'id';
exec sys.sp_addextendedproperty
@name = N'column_comment_public_users_id',
@value = N'user''s comment',
exec sys.sp_addextendedproperty
@name = N'column_comment_public_users_id',
@value = N'user''s comment',
@level0type = N'SCHEMA', @level0name = 'public'
,@level1type = N'TABLE', @level1name = 'users',@level2type = N'COLUMN', @level2name = 'id'"
`;
@ -36,7 +36,7 @@ exports[`mssql datasource tests getAlterFKSql should generate SQL query for alte
ALTER TABLE "dbo"."user"
ADD CONSTRAINT "newConstraint"
FOREIGN KEY (id, id2)
REFERENCES "dbo"."user1" (id, id2)
REFERENCES "dbo"."user1" (id, id2)
ON UPDATE cascade ON DELETE cascade;
COMMIT transaction;
"
@ -50,7 +50,7 @@ exports[`mssql datasource tests getAlterFKSql should generate SQL query for alte
ALTER TABLE "dbo"."user"
ADD CONSTRAINT "newConstraint"
FOREIGN KEY (id)
REFERENCES "dbo"."user1" (id)
REFERENCES "dbo"."user1" (id)
ON UPDATE no action ON DELETE cascade;
COMMIT transaction;
"
@ -61,7 +61,7 @@ exports[`mssql datasource tests getAlterPkSql should generate alter operation as
ALTER TABLE "public"."users" DROP CONSTRAINT "PK__users__1234";
ALTER TABLE "public"."users"
ADD CONSTRAINT "PK__users__1234" PRIMARY KEY ("id");
COMMIT TRANSACTION;"
`;
@ -70,7 +70,7 @@ exports[`mssql datasource tests getAlterPkSql should work with multi-column PKs
ALTER TABLE "public"."users" DROP CONSTRAINT "test_constraint";
ALTER TABLE "public"."users"
ADD CONSTRAINT "test_constraint" PRIMARY KEY ("id", "account");
COMMIT TRANSACTION;"
`;

View File

@ -528,10 +528,10 @@ FROM sys.objects as obj
// add comment to the table using MS_Description property
if (tableComment && tableComment !== '') {
const commentStr = sqlHandleApostrophe(tableComment);
const commentSQL = `EXEC sys.sp_addextendedproperty
@name = N'MS_Description',
@value = N'${commentStr}',
@level0type = N'SCHEMA', @level0name = '${currentSchema}',
const commentSQL = `EXEC sys.sp_addextendedproperty
@name = N'MS_Description',
@value = N'${commentStr}',
@level0type = N'SCHEMA', @level0name = '${currentSchema}',
@level1type = N'TABLE', @level1name = '${tableName}';`;
sqlCreateTable += `${commentSQL}`;
}
@ -574,7 +574,7 @@ FROM sys.objects as obj
ALTER TABLE "${from.schemaName}"."${from.tableName}"
ADD CONSTRAINT "${newConstraint}"
FOREIGN KEY (${from.columns.join(', ')})
REFERENCES "${to.schemaName}"."${to.tableName}" (${to.columns.join(', ')})
REFERENCES "${to.schemaName}"."${to.tableName}" (${to.columns.join(', ')})
ON UPDATE ${onUpdate} ON DELETE ${onDelete};
COMMIT transaction;
`;
@ -763,7 +763,7 @@ FROM sys.objects as obj
ADD CONSTRAINT "${constraintName}" PRIMARY KEY (${selectedPkColumns
.map(pkc => `"${pkc}"`)
.join(', ')});
COMMIT TRANSACTION;`;
},
getFunctionDefinitionSql: null,
@ -914,13 +914,13 @@ INNER JOIN sys.schemas sch2
eventLogTable: QualifiedTable,
eventId: string
) => {
const sql = `SELECT CONVERT(varchar(MAX), original_table.id) AS "id", CONVERT(varchar(MAX), original_table.event_id) AS "event_id",
original_table.status, CONVERT(varchar(MAX), original_table.request) AS "request", CONVERT(varchar(MAX), original_table.response) AS "response",
CONVERT(varchar(MAX), CAST(original_table.created_at as datetime2)) AS "created_at", CONVERT(varchar(MAX), data_table.id) AS "id", data_table.schema_name,
const sql = `SELECT CONVERT(varchar(MAX), original_table.id) AS "id", CONVERT(varchar(MAX), original_table.event_id) AS "event_id",
original_table.status, CONVERT(varchar(MAX), original_table.request) AS "request", CONVERT(varchar(MAX), original_table.response) AS "response",
CONVERT(varchar(MAX), CAST(original_table.created_at as datetime2)) AS "created_at", CONVERT(varchar(MAX), data_table.id) AS "id", data_table.schema_name,
data_table.table_name, data_table.trigger_name, CONVERT(varchar(MAX), data_table.payload) AS "payload", data_table.delivered, data_table.error,
data_table.tries, CONVERT(varchar(MAX), CAST(data_table.created_at as datetime2)) AS "created_at", CONVERT(varchar(MAX), data_table.locked) AS "locked",
CONVERT(varchar(MAX), data_table.next_retry_at) AS "next_retry_at", data_table.archived
FROM "${logTableDef.schema}"."${logTableDef.name}" AS original_table JOIN "${eventLogTable.schema}"."${eventLogTable.name}"
data_table.tries, CONVERT(varchar(MAX), CAST(data_table.created_at as datetime2)) AS "created_at", CONVERT(varchar(MAX), data_table.locked) AS "locked",
CONVERT(varchar(MAX), data_table.next_retry_at) AS "next_retry_at", data_table.archived
FROM "${logTableDef.schema}"."${logTableDef.name}" AS original_table JOIN "${eventLogTable.schema}"."${eventLogTable.name}"
AS data_table ON original_table.event_id = data_table.id
WHERE original_table.event_id = '${eventId}'
ORDER BY original_table.created_at DESC `;
@ -998,15 +998,15 @@ WHERE
comment,
}) => {
const commentStr = sqlHandleApostrophe(comment);
const dropCommonCommentStatement = `IF EXISTS (SELECT NULL FROM SYS.EXTENDED_PROPERTIES WHERE [major_id] = OBJECT_ID('${tableName}') AND [name] = N'column_comment_${schemaName}_${tableName}_${columnName}' AND [minor_id] = (SELECT [column_id] FROM SYS.COLUMNS WHERE [name] = '${columnName}' AND [object_id] = OBJECT_ID('${tableName}')))
EXECUTE sp_dropextendedproperty
@name = N'column_comment_${schemaName}_${tableName}_${columnName}',
const dropCommonCommentStatement = `IF EXISTS (SELECT NULL FROM SYS.EXTENDED_PROPERTIES WHERE [major_id] = OBJECT_ID('${tableName}') AND [name] = N'column_comment_${schemaName}_${tableName}_${columnName}' AND [minor_id] = (SELECT [column_id] FROM SYS.COLUMNS WHERE [name] = '${columnName}' AND [object_id] = OBJECT_ID('${tableName}')))
EXECUTE sp_dropextendedproperty
@name = N'column_comment_${schemaName}_${tableName}_${columnName}',
@level0type = N'SCHEMA', @level0name = '${schemaName}'
`;
const commonCommentStatement = `
exec sys.sp_addextendedproperty
@name = N'column_comment_${schemaName}_${tableName}_${columnName}',
@value = N'${commentStr}',
exec sys.sp_addextendedproperty
@name = N'column_comment_${schemaName}_${tableName}_${columnName}',
@value = N'${commentStr}',
@level0type = N'SCHEMA', @level0name = '${schemaName}'
`;
return `${dropCommonCommentStatement},@level1type = N'TABLE', @level1name = '${tableName}',@level2type = N'COLUMN', @level2name = '${columnName}';
@ -1022,11 +1022,11 @@ WHERE
// https://github.com/hasura/graphql-engine-mono/issues/4641
getDataTriggerInvocations: (eventId: string): string => {
const eventInvTable = `"hdb_catalog"."event_invocation_logs"`;
const sql = `SELECT CONVERT(varchar(MAX), id) AS "id", CONVERT(varchar(MAX), event_id) AS "event_id",
status, CONVERT(varchar(MAX), request) AS "request", CONVERT(varchar(MAX), response) AS "response",
const sql = `SELECT CONVERT(varchar(MAX), id) AS "id", CONVERT(varchar(MAX), event_id) AS "event_id",
status, CONVERT(varchar(MAX), request) AS "request", CONVERT(varchar(MAX), response) AS "response",
CONVERT(varchar(MAX), CAST(created_at as datetime2)) AS "created_at"
FROM ${eventInvTable}
WHERE event_id = '${eventId}'
FROM ${eventInvTable}
WHERE event_id = '${eventId}'
ORDER BY created_at DESC;`;
return sql;
},

View File

@ -1,6 +1,7 @@
import {
getFullQueryNameBase,
getGraphQLQueryBase,
getQueryWithNamespace,
QueryBody,
} from './../../common';
import { TableConfig } from './../../../metadata/types';
@ -8,6 +9,8 @@ import Endpoints from '../../../Endpoints';
import { ReduxState } from '../../../types';
import { Relationship } from '../../types';
import { isEmpty } from '../../../components/Common/utils/jsUtils';
import { SourceCustomization } from '../../../features/hasura-metadata-types';
import { Column, getQueryName } from '../../common/utils';
type Tables = ReduxState['tables'];
@ -81,21 +84,36 @@ const getFormattedValue = (
};
const getColQuery = (
cols: (string | { name: string; columns: string[] })[],
cols: (string | Column)[],
limit: number,
relationships: Relationship[],
tableConfiguration: TableConfig
tableConfiguration: TableConfig,
dataSourceCustomization: SourceCustomization
): string[] => {
return cols.map(c => {
return cols.map(column => {
const columnConfig = tableConfiguration?.column_config ?? {};
if (typeof c === 'string') return columnConfig[c]?.custom_name ?? c;
const rel = relationships.find((r: any) => r.rel_name === c.name);
return `${columnConfig[c.name]?.custom_name ?? c.name} ${
if (typeof column === 'string') {
return columnConfig[column]?.custom_name ?? column;
}
const queryName = getQueryName({
column,
tableConfiguration,
dataSourceCustomization,
});
const rel = relationships.find((r: any) => r.rel_name === column.name);
return `${queryName} ${
rel?.rel_type === 'array' ? `(limit: ${limit})` : ''
} {
${getColQuery(c.columns, limit, relationships, tableConfiguration).join(
'\n'
)} }`;
${getColQuery(
column.columns,
limit,
relationships,
tableConfiguration,
dataSourceCustomization
).join('\n')} }`;
});
};
@ -103,10 +121,12 @@ export const getTableRowRequestBody = ({
tables,
isExport,
tableConfiguration,
dataSourceCustomization,
}: {
tables: Tables;
isExport?: boolean;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => {
const {
currentTable: originalTable,
@ -119,30 +139,41 @@ export const getTableRowRequestBody = ({
tableName,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select',
});
const aggregateName = getFullQueryName({
tableName,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select_aggregate',
});
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const queryBody = ({ clauses, relationshipInfo }: QueryBody) => {
return `query TableRows {
${queryName} ${clauses && `(${clauses})`} {
return getQueryWithNamespace({
queryName: 'query TableRows',
namespace,
innerQuery: `
${queryName} ${clauses && `(${clauses})`}
{
${getColQuery(
view.query.columns,
view.curFilter.limit,
relationshipInfo,
tableConfiguration
tableConfiguration,
dataSourceCustomization
).join('\n')}
}
${aggregateName} {
aggregate {
count
}
}
}`;
${aggregateName}
{
aggregate {
count
}
}
`,
});
};
return {
@ -167,9 +198,15 @@ const processTableRowData = (
originalTable: string;
currentSchema: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}
) => {
const { originalTable, currentSchema, tableConfiguration } = config!;
const {
originalTable,
currentSchema,
tableConfiguration,
dataSourceCustomization,
} = config!;
const reversedCustomColumns = Object.entries(
tableConfiguration?.column_config ?? {}
@ -183,9 +220,16 @@ const processTableRowData = (
tableName,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select',
});
const results = data?.data[queryName];
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
const hasNamespace = !!namespace;
const results = hasNamespace
? data?.data[namespace][queryName]
: data?.data[queryName];
const rows = isEmpty(reversedCustomColumns)
? results
@ -212,9 +256,11 @@ export const generateTableRowRequest = () => ({
export const getRowsCountRequestBody = ({
tables,
tableConfiguration,
dataSourceCustomization,
}: {
tables: Tables;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => {
const {
currentTable: originalTable,
@ -227,15 +273,22 @@ export const getRowsCountRequestBody = ({
tableName: originalTable,
schema: currentSchema,
tableConfiguration,
dataSourceCustomization,
operation: 'select_aggregate',
});
return `query TableCount {
${queryName} ${clauses && `(${clauses})`} {
aggregate {
count
const namespace = dataSourceCustomization?.root_fields?.namespace ?? '';
return getQueryWithNamespace({
queryName: 'query TableCount',
namespace: namespace,
innerQuery: `
${queryName} ${clauses && `(${clauses})`} {
aggregate {
count
}
}
}
}`;
`,
});
};
return {
@ -259,12 +312,14 @@ const processCount = (c: {
currentSchema: string;
originalTable: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}): number => {
const key = getFullQueryName({
tableName: c.originalTable,
schema: c.currentSchema,
tableConfiguration: c.tableConfiguration,
operation: 'select_aggregate',
dataSourceCustomization: c.dataSourceCustomization,
});
return c.data?.data?.[key]?.aggregate?.count;
};

View File

@ -249,7 +249,7 @@ export const getAlterColumnTypeSql = (
alter table ${getMySQLNameString(
schemaName,
tableName
)} modify column \`${columnName}\` ${columnType};
)} modify column \`${columnName}\` ${columnType};
`;
export const getDropColumnDefaultSql = (

View File

@ -14,6 +14,7 @@ import {
} from '../components/Common/utils/v1QueryUtils';
import { Driver } from '.';
import { PostgresTrigger } from './services/postgresql/types';
import { SourceCustomization } from '../features/hasura-metadata-types';
export type TableORSchemaArg =
| { schemas: string[] }
@ -444,6 +445,7 @@ export type generateTableRowRequestType = {
tables: Tables;
isExport?: boolean;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) =>
| {
type: string;
@ -464,6 +466,7 @@ export type generateTableRowRequestType = {
originalTable: string;
currentSchema: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}
) => { rows: T[]; estimatedCount: number };
};
@ -475,6 +478,7 @@ export type generateInsertRequestType = {
source: string;
insertObject: Record<string, any>;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
returning: string[];
}) =>
| {
@ -495,6 +499,7 @@ export type generateInsertRequestType = {
| { affectedRows: number; returning: Array<Record<string, any>> }
| Record<string, Record<string, any>>,
tableConfiguration: TableConfig,
dataSourceCustomization: SourceCustomization,
config: {
currentTable: string;
currentSchema: string;
@ -513,6 +518,7 @@ export type GenerateRowsCountRequestType = {
originalTable: string;
currentSchema: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) => number;
};
@ -521,12 +527,14 @@ export type GenerateEditRowRequest = {
processEditData: (args: {
tableDef: QualifiedTable;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
data: any;
}) => number;
getEditRowRequestBody: (data: {
source: string;
tableDef: QualifiedTable;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
set: Record<string, any>;
where: Record<string, any>;
defaultArray: any[];
@ -567,6 +575,7 @@ export type GenerateDeleteRowRequest = {
columnInfo: BaseTableColumn[];
source: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) =>
| {
type: string;
@ -583,7 +592,12 @@ export type GenerateDeleteRowRequest = {
query: string;
variables: null;
};
processDeleteRowData: (data: Record<string, any>) => number;
processDeleteRowData: (
data: Record<string, any>,
config: {
dataSourceCustomization: SourceCustomization;
}
) => number;
};
export type GenerateBulkDeleteRowRequest = {
@ -595,6 +609,7 @@ export type GenerateBulkDeleteRowRequest = {
columnInfo: BaseTableColumn[];
source: string;
tableConfiguration: TableConfig;
dataSourceCustomization: SourceCustomization;
}) =>
| {
type: string;
@ -615,7 +630,10 @@ export type GenerateBulkDeleteRowRequest = {
query: string;
variables: null;
};
processBulkDeleteRowData: (data: Record<string, any>) => number;
processBulkDeleteRowData: (
data: Record<string, any>,
config: { dataSourceCustomization: SourceCustomization }
) => number;
};
export type ViolationActions =
| 'restrict'