mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-11-10 10:29:12 +03:00
console: show only compatible postgres functions in computed fields section (close #5155)
GITHUB_PR_NUMBER: 5978 GITHUB_PR_URL: https://github.com/hasura/graphql-engine/pull/5978 Co-authored-by: Dmitry Grachikov <696824+GrizliK1988@users.noreply.github.com> Co-authored-by: Aleksandra Sikora <9019397+beerose@users.noreply.github.com> GitOrigin-RevId: 9399fae17ab3985fa0dd0339b6a36f0ac57997fa
This commit is contained in:
parent
353859db09
commit
3cac1c30c0
@ -108,6 +108,7 @@ and be accessible according to the permissions that were configured for the role
|
||||
- console: misc bug fixes (close #4785, #6330, #6288)
|
||||
- console: allow setting table custom name (#212)
|
||||
- console: support tracking VOLATILE functions as mutations or queries (close #6228)
|
||||
- console: show only compatible postgres functions in computed fields section (close #5155) (#5978)
|
||||
- console: added export data option on browse rows page (close #1438 #5158)
|
||||
- cli: add missing global flags for seed command (#5565)
|
||||
- cli: allow seeds as alias for seed command (#5693)
|
||||
|
@ -146,6 +146,39 @@ export const testCustomFunctionSQLWithSessArg = (
|
||||
};
|
||||
};
|
||||
|
||||
export const createUntrackedFunctionSQL = (
|
||||
fnName: string,
|
||||
tableName: string
|
||||
) => {
|
||||
return {
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
sql: `
|
||||
CREATE OR REPLACE FUNCTION ${fnName}(table_row "${tableName}")
|
||||
RETURNS int
|
||||
LANGUAGE sql
|
||||
STABLE
|
||||
AS $function$
|
||||
SELECT table_row.id
|
||||
$function$
|
||||
`,
|
||||
cascade: false,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const dropUntrackedFunctionSQL = (fnName: string) => {
|
||||
return {
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
sql: `
|
||||
DROP FUNCTION public.${fnName};
|
||||
`,
|
||||
cascade: false,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getTrackFnPayload = (name = 'customfunctionwithsessionarg') => ({
|
||||
type: 'pg_track_function',
|
||||
args: {
|
||||
|
@ -4,6 +4,8 @@ import {
|
||||
getTableName,
|
||||
getColName,
|
||||
getElementFromAlias,
|
||||
createUntrackedFunctionSQL,
|
||||
dropUntrackedFunctionSQL,
|
||||
} from '../../../helpers/dataHelpers';
|
||||
|
||||
import {
|
||||
@ -11,11 +13,37 @@ import {
|
||||
validateCT,
|
||||
validateColumn,
|
||||
ResultType,
|
||||
dataRequest,
|
||||
} from '../../validators/validators';
|
||||
import { setPromptValue } from '../../../helpers/common';
|
||||
|
||||
const testName = 'mod';
|
||||
|
||||
export const passMTFunctionList = () => {
|
||||
const tableName = getTableName(0, testName);
|
||||
dataRequest(
|
||||
createUntrackedFunctionSQL(`${tableName}_id_fn`, tableName),
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
cy.wait(5000);
|
||||
cy.get(getElementFromAlias('modify-table-edit-computed-field-0')).click();
|
||||
|
||||
cy.get(getElementFromAlias('functions-dropdown')).click();
|
||||
|
||||
cy.get('[data-test^="data_test_column_type_value_"]').should(
|
||||
'have.length',
|
||||
1
|
||||
);
|
||||
|
||||
cy.get('[data-test^="data_test_column_type_value_"]')
|
||||
.first()
|
||||
.should('have.text', `${getTableName(0, testName)}_id_fn`.toLowerCase());
|
||||
dataRequest(
|
||||
dropUntrackedFunctionSQL(`${tableName}_id_fn`),
|
||||
ResultType.SUCCESS
|
||||
);
|
||||
};
|
||||
|
||||
export const passMTCreateTable = () => {
|
||||
cy.get(getElementFromAlias('data-create-table')).click();
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public/table/add`);
|
||||
@ -30,7 +58,10 @@ export const passMTCreateTable = () => {
|
||||
cy.wait(7000);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(0, testName)}/modify`
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
validateCT(getTableName(0, testName), ResultType.SUCCESS);
|
||||
};
|
||||
@ -41,7 +72,10 @@ export const passMTCheckRoute = () => {
|
||||
// Match the URL
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(0, testName)}/modify`
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
};
|
||||
|
||||
@ -92,7 +126,10 @@ export const passMTMoveToTable = () => {
|
||||
cy.get(getElementFromAlias(getTableName(0, testName))).click();
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(0, testName)}/browse`
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/browse`
|
||||
);
|
||||
};
|
||||
|
||||
@ -101,7 +138,10 @@ export const failMTWithoutColName = () => {
|
||||
cy.get(getElementFromAlias('modify-table-add-new-column-save')).click();
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(0, testName)}/modify`
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
|
||||
validateColumn(
|
||||
@ -116,7 +156,10 @@ export const failMTWithoutColType = () => {
|
||||
cy.get(getElementFromAlias('modify-table-add-new-column-save')).click();
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(0, testName)}/modify`
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
validateColumn(
|
||||
getTableName(0, testName),
|
||||
@ -137,7 +180,10 @@ export const Addcolumnnullable = () => {
|
||||
cy.wait(2500);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(0, testName)}/modify`
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
validateColumn(
|
||||
getTableName(0, testName),
|
||||
@ -266,7 +312,10 @@ export const passMTDeleteCol = () => {
|
||||
cy.wait(5000);
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(0, testName)}/modify`
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
validateColumn(
|
||||
getTableName(0, testName),
|
||||
@ -281,7 +330,10 @@ export const passMTDeleteTableCancel = () => {
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.url().should(
|
||||
'eq',
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(0, testName)}/modify`
|
||||
`${baseUrl}/data/default/schema/public/tables/${getTableName(
|
||||
0,
|
||||
testName
|
||||
)}/modify`
|
||||
);
|
||||
|
||||
validateCT(getTableName(0, testName), ResultType.SUCCESS);
|
||||
@ -292,8 +344,7 @@ export const passMTDeleteTable = () => {
|
||||
cy.get(getElementFromAlias('delete-table')).click();
|
||||
cy.window().its('prompt').should('be.called');
|
||||
cy.wait(5000);
|
||||
// FIXME: change this later
|
||||
// cy.url().should('eq', `${baseUrl}/data/default/schema/public`);
|
||||
cy.url().should('eq', `${baseUrl}/data/default/schema/public`);
|
||||
validateCT(getTableName(0, testName), ResultType.FAILURE);
|
||||
};
|
||||
|
||||
|
@ -19,6 +19,7 @@ import {
|
||||
passModifyUniqueKey,
|
||||
passRemoveUniqueKey,
|
||||
passMTChangeDefaultValueForPKey,
|
||||
passMTFunctionList,
|
||||
} from './spec';
|
||||
|
||||
import { testMode } from '../../../helpers/common';
|
||||
@ -42,6 +43,10 @@ export const runModifyTableTests = () => {
|
||||
it('Creating a table', passMTCreateTable);
|
||||
it('Moving to the table', passMTMoveToTable);
|
||||
it('Modify table button opens the correct route', passMTCheckRoute);
|
||||
it(
|
||||
'Can create computed field with compatible functions',
|
||||
passMTFunctionList
|
||||
);
|
||||
it('Pass renaming table', passMTRenameTable);
|
||||
it('Pass renaming column', passMTRenameColumn);
|
||||
it('Fails to add column without column name', failMTWithoutColName);
|
||||
|
@ -311,10 +311,18 @@ const ComputedFieldsEditor = ({
|
||||
Create new
|
||||
</RawSqlButton>
|
||||
</div>
|
||||
<div className={styles.wd50percent}>
|
||||
<div
|
||||
className={styles.wd50percent}
|
||||
data-test={'functions-dropdown'}
|
||||
>
|
||||
<SearchableSelectBox
|
||||
options={dataSource
|
||||
.getSchemaFunctions(functions, computedFieldFunctionSchema)
|
||||
.getSchemaFunctions(
|
||||
functions,
|
||||
computedFieldFunctionSchema,
|
||||
table.table_name,
|
||||
table.table_schema
|
||||
)
|
||||
.map(fn => fn.function_name)}
|
||||
onChange={handleFnNameChange}
|
||||
value={computedFieldFunctionName}
|
||||
|
@ -31,7 +31,12 @@ export interface DataSourcesAPI {
|
||||
// todo: replace with function type
|
||||
getFunctionSchema(func: PGFunction): string;
|
||||
getFunctionDefinition(func: PGFunction): string;
|
||||
getSchemaFunctions(func: PGFunction[], schemaName: string): PGFunction[];
|
||||
getSchemaFunctions(
|
||||
func: PGFunction[],
|
||||
schemaName: string,
|
||||
tableName: string,
|
||||
tableSchema: string
|
||||
): PGFunction[];
|
||||
findFunction(
|
||||
func: PGFunction[],
|
||||
fName: string,
|
||||
|
@ -135,11 +135,42 @@ export const getFunctionDefinition = (pgFunction: PGFunction) => {
|
||||
return pgFunction.function_definition;
|
||||
};
|
||||
|
||||
export const isFunctionCompatibleToTable = (
|
||||
pgFunction: PGFunction,
|
||||
tableName: string,
|
||||
tableSchema: string
|
||||
) => {
|
||||
const inputArgTypes = pgFunction?.input_arg_types || [];
|
||||
|
||||
let hasTableRowInArguments = false;
|
||||
let hasUnsupportedArguments = false;
|
||||
|
||||
inputArgTypes.forEach(inputArgType => {
|
||||
if (!hasTableRowInArguments) {
|
||||
hasTableRowInArguments =
|
||||
inputArgType.name === tableName && inputArgType.schema === tableSchema;
|
||||
}
|
||||
|
||||
if (!hasUnsupportedArguments) {
|
||||
hasUnsupportedArguments =
|
||||
inputArgType.type !== 'c' && inputArgType.type !== 'b';
|
||||
}
|
||||
});
|
||||
|
||||
return hasTableRowInArguments && !hasUnsupportedArguments;
|
||||
};
|
||||
|
||||
export const getSchemaFunctions = (
|
||||
allFunctions: PGFunction[],
|
||||
fnSchema: string
|
||||
fnSchema: string,
|
||||
tableName: string,
|
||||
tableSchema: string
|
||||
) => {
|
||||
return allFunctions.filter(fn => getFunctionSchema(fn) === fnSchema);
|
||||
return allFunctions.filter(
|
||||
fn =>
|
||||
getFunctionSchema(fn) === fnSchema &&
|
||||
isFunctionCompatibleToTable(fn, tableName, tableSchema)
|
||||
);
|
||||
};
|
||||
|
||||
export const findFunction = (
|
||||
|
@ -1,9 +1,16 @@
|
||||
export type PGInputArgType = {
|
||||
schema: string;
|
||||
name: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export type PGFunction = {
|
||||
function_name: string;
|
||||
function_schema: string;
|
||||
function_definition: string;
|
||||
return_type_type: string;
|
||||
function_type: string;
|
||||
input_arg_types?: PGInputArgType[];
|
||||
};
|
||||
|
||||
export interface PostgresTable {
|
||||
|
Loading…
Reference in New Issue
Block a user