mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
parent
5eb9d8887e
commit
dc1748b539
@ -53,6 +53,7 @@ export const unTrackFunction = () => {
|
||||
};
|
||||
|
||||
export const trackFunction = () => {
|
||||
cy.get(getElementFromAlias('toggle-trackable-functions')).click();
|
||||
cy.get(
|
||||
getElementFromAlias(`add-track-function-${getCustomFunctionName(1)}`)
|
||||
).should('exist');
|
||||
|
@ -69,6 +69,10 @@ export const getTrackedTables = tables => {
|
||||
return tables.filter(t => t.is_table_tracked);
|
||||
};
|
||||
|
||||
export const getUntrackedTables = tables => {
|
||||
return tables.filter(t => !t.is_table_tracked);
|
||||
};
|
||||
|
||||
export const getOnlyTables = tablesOrViews => {
|
||||
return tablesOrViews.filter(t => checkIfTable(t));
|
||||
};
|
||||
|
@ -101,8 +101,9 @@ const initQueries = {
|
||||
schema: 'hdb_catalog',
|
||||
},
|
||||
columns: ['function_name', 'function_schema', 'is_system_defined'],
|
||||
order_by: [{ column: 'function_name', type: 'asc', nulls: 'last' }],
|
||||
where: {
|
||||
function_schema: '',
|
||||
function_schema: '', // needs to be set later
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -128,8 +129,9 @@ const initQueries = {
|
||||
columns: ['table_schema', 'table_name'],
|
||||
},
|
||||
],
|
||||
order_by: [{ column: 'function_name', type: 'asc', nulls: 'last' }],
|
||||
where: {
|
||||
function_schema: '',
|
||||
function_schema: '', // needs to be set later
|
||||
has_variadic: false,
|
||||
returns_set: true,
|
||||
return_type_type: compositeFnCheck, // COMPOSITE type
|
||||
@ -165,15 +167,30 @@ const initQueries = {
|
||||
'return_type_name',
|
||||
'return_type_type',
|
||||
'returns_set',
|
||||
{
|
||||
name: 'return_table_info',
|
||||
columns: ['table_schema', 'table_name'],
|
||||
},
|
||||
],
|
||||
order_by: [{ column: 'function_name', type: 'asc', nulls: 'last' }],
|
||||
where: {
|
||||
// TODO: set correct where
|
||||
function_schema: '',
|
||||
has_variadic: false,
|
||||
returns_set: true,
|
||||
return_type_type: compositeFnCheck, // COMPOSITE type
|
||||
function_type: {
|
||||
$ilike: '%volatile%',
|
||||
function_schema: '', // needs to be set later
|
||||
$not: {
|
||||
has_variadic: false,
|
||||
returns_set: true,
|
||||
return_type_type: compositeFnCheck, // COMPOSITE type
|
||||
$or: [
|
||||
{
|
||||
function_type: {
|
||||
$ilike: '%stable%',
|
||||
},
|
||||
},
|
||||
{
|
||||
function_type: {
|
||||
$ilike: '%immutable%',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -389,10 +406,12 @@ const fetchFunctionInit = () => (dispatch, getState) => {
|
||||
headers: dataHeaders(getState),
|
||||
body: JSON.stringify(body),
|
||||
};
|
||||
|
||||
return dispatch(requestAction(url, options)).then(
|
||||
data => {
|
||||
dispatch({ type: LOAD_FUNCTIONS, data: data[0] });
|
||||
dispatch({ type: LOAD_NON_TRACKABLE_FUNCTIONS, data: data[1] });
|
||||
|
||||
let consistentFunctions = data[2];
|
||||
const { inconsistentObjects } = getState().metadata;
|
||||
if (inconsistentObjects.length > 0) {
|
||||
|
@ -37,7 +37,15 @@ import { createNewSchema, deleteCurrentSchema } from './Actions';
|
||||
import CollapsibleToggle from '../../../Common/CollapsibleToggle/CollapsibleToggle';
|
||||
import gqlPattern from '../Common/GraphQLValidation';
|
||||
import GqlCompatibilityWarning from '../../../Common/GqlCompatibilityWarning/GqlCompatibilityWarning';
|
||||
import { displayTableName } from '../../../Common/utils/pgUtils';
|
||||
import {
|
||||
displayTableName,
|
||||
getFunctionName,
|
||||
getSchemaTables,
|
||||
getUntrackedTables,
|
||||
} from '../../../Common/utils/pgUtils';
|
||||
import { SET_SQL } from '../RawSQL/Actions';
|
||||
import _push from '../push';
|
||||
import { isEmpty } from '../../../Common/utils/jsUtils';
|
||||
import { getConfirmation } from '../../../Common/utils/jsUtils';
|
||||
|
||||
class Schema extends Component {
|
||||
@ -77,15 +85,15 @@ class Schema extends Component {
|
||||
|
||||
/***********/
|
||||
|
||||
const getTrackableFunctions = () => {
|
||||
const trackedFuncNames = trackedFunctions.map(t => t.function_name);
|
||||
const _getTrackableFunctions = () => {
|
||||
const trackedFuncNames = trackedFunctions.map(fn => getFunctionName(fn));
|
||||
|
||||
// Assuming schema for both function and tables are same
|
||||
// return function which are tracked && function name whose
|
||||
// set of tables are tracked
|
||||
const filterCondition = func => {
|
||||
return (
|
||||
!trackedFuncNames.includes(func.function_name) &&
|
||||
!trackedFuncNames.includes(getFunctionName(func)) &&
|
||||
!!func.return_table_info
|
||||
);
|
||||
};
|
||||
@ -93,25 +101,6 @@ class Schema extends Component {
|
||||
return functionsList.filter(filterCondition);
|
||||
};
|
||||
|
||||
const getUntrackedTables = () => {
|
||||
const tableSortFunc = (a, b) => {
|
||||
return a.table_name === b.table_name
|
||||
? 0
|
||||
: +(a.table_name > b.table_name) || -1;
|
||||
};
|
||||
|
||||
const _untrackedTables = schema.filter(
|
||||
table => !table.is_table_tracked && table.table_schema === currentSchema
|
||||
);
|
||||
|
||||
// update tableInfo with graphql compatibility
|
||||
_untrackedTables.forEach(t => {
|
||||
t.isGQLCompatible = gqlPattern.test(t.table_name);
|
||||
});
|
||||
|
||||
return _untrackedTables.sort(tableSortFunc);
|
||||
};
|
||||
|
||||
const getSectionHeading = (headingText, tooltip, actionBtn = null) => {
|
||||
return (
|
||||
<div>
|
||||
@ -132,8 +121,29 @@ class Schema extends Component {
|
||||
|
||||
/***********/
|
||||
|
||||
const allUntrackedTables = getUntrackedTables();
|
||||
const trackableFuncs = getTrackableFunctions();
|
||||
const allUntrackedTables = getUntrackedTables(
|
||||
getSchemaTables(schema, currentSchema)
|
||||
);
|
||||
const trackableFuncs = _getTrackableFunctions();
|
||||
|
||||
const getTrackableFunctionsRequirementsMessage = () => {
|
||||
const requirementsLink = (
|
||||
<a
|
||||
href="https://docs.hasura.io/1.0/graphql/manual/queries/custom-functions.html#supported-sql-functions"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
requirements
|
||||
</a>
|
||||
);
|
||||
|
||||
return (
|
||||
<i>
|
||||
See {requirementsLink} for functions to be exposed over the GraphQL
|
||||
API
|
||||
</i>
|
||||
);
|
||||
};
|
||||
|
||||
const getCreateBtn = () => {
|
||||
let createBtn = null;
|
||||
@ -359,7 +369,8 @@ class Schema extends Component {
|
||||
dispatch(addExistingTableSql());
|
||||
};
|
||||
|
||||
const gqlCompatibilityWarning = !table.isGQLCompatible ? (
|
||||
const isGQLCompatible = gqlPattern.test(table.table_name);
|
||||
const gqlCompatibilityWarning = !isGQLCompatible ? (
|
||||
<span className={styles.add_mar_left_mid}>
|
||||
<GqlCompatibilityWarning />
|
||||
</span>
|
||||
@ -523,55 +534,69 @@ class Schema extends Component {
|
||||
};
|
||||
|
||||
const getUntrackedFunctionsSection = () => {
|
||||
let trackableFunctionList = null;
|
||||
const noTrackableFunctions = isEmpty(trackableFuncs);
|
||||
|
||||
if (trackableFuncs.length > 0) {
|
||||
const heading = getSectionHeading(
|
||||
'Untracked custom functions',
|
||||
trackableFunctionsTip
|
||||
);
|
||||
const getTrackableFunctionsList = () => {
|
||||
const trackableFunctionList = [];
|
||||
|
||||
trackableFunctionList = (
|
||||
<div className={styles.add_mar_top} key={'custom-functions-content'}>
|
||||
<CollapsibleToggle title={heading} isOpen>
|
||||
<div className={`${styles.padd_left_remove} col-xs-12`}>
|
||||
{trackableFuncs.map((p, i) => (
|
||||
<div
|
||||
className={styles.padd_bottom}
|
||||
key={`${i}untracked-function`}
|
||||
>
|
||||
<div
|
||||
className={`${styles.display_inline} ${
|
||||
styles.add_mar_right
|
||||
}`}
|
||||
>
|
||||
<Button
|
||||
data-test={`add-track-function-${p.function_name}`}
|
||||
className={`${
|
||||
styles.display_inline
|
||||
} btn btn-xs btn-default`}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
trackableFuncs.forEach((p, i) => {
|
||||
trackableFunctionList.push(
|
||||
<div className={styles.padd_bottom} key={`untracked-function-${i}`}>
|
||||
<div
|
||||
className={`${styles.display_inline} ${styles.add_mar_right}`}
|
||||
>
|
||||
<Button
|
||||
data-test={`add-track-function-${p.function_name}`}
|
||||
className={`${styles.display_inline} btn btn-xs btn-default`}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
|
||||
dispatch(addExistingFunction(p.function_name));
|
||||
}}
|
||||
>
|
||||
Track
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.display_inline}>
|
||||
<span>{p.function_name}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
dispatch(addExistingFunction(p.function_name));
|
||||
}}
|
||||
>
|
||||
Track
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.clear_fix} />
|
||||
</CollapsibleToggle>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<div className={styles.display_inline}>
|
||||
<span>{p.function_name}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return trackableFunctionList;
|
||||
if (noTrackableFunctions) {
|
||||
trackableFunctionList.push(
|
||||
<div key="no-untracked-fns">
|
||||
<div>There are no untracked functions</div>
|
||||
<div className={styles.add_mar_top}>
|
||||
{getTrackableFunctionsRequirementsMessage()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return trackableFunctionList;
|
||||
};
|
||||
|
||||
const heading = getSectionHeading(
|
||||
'Untracked custom functions',
|
||||
trackableFunctionsTip
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.add_mar_top} key={'custom-functions-content'}>
|
||||
<CollapsibleToggle
|
||||
title={heading}
|
||||
isOpen={!noTrackableFunctions}
|
||||
testId={'toggle-trackable-functions'}
|
||||
>
|
||||
<div className={`${styles.padd_left_remove} col-xs-12`}>
|
||||
{getTrackableFunctionsList()}
|
||||
</div>
|
||||
<div className={styles.clear_fix} />
|
||||
</CollapsibleToggle>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getNonTrackableFunctionsSection = () => {
|
||||
@ -579,7 +604,7 @@ class Schema extends Component {
|
||||
|
||||
if (nonTrackableFunctions.length > 0) {
|
||||
const heading = getSectionHeading(
|
||||
'Non trackable custom functions',
|
||||
'Non trackable functions',
|
||||
nonTrackableFunctionsTip
|
||||
);
|
||||
|
||||
@ -588,18 +613,41 @@ class Schema extends Component {
|
||||
className={styles.add_mar_top}
|
||||
key={'non-trackable-custom-functions'}
|
||||
>
|
||||
<CollapsibleToggle title={heading} isOpen>
|
||||
<CollapsibleToggle title={heading} isOpen={false}>
|
||||
<div className={`${styles.padd_left_remove} col-xs-12`}>
|
||||
<div className={styles.add_mar_bottom}>
|
||||
{getTrackableFunctionsRequirementsMessage()}
|
||||
</div>
|
||||
{nonTrackableFunctions.map((p, i) => (
|
||||
<div
|
||||
className={styles.padd_bottom}
|
||||
key={`${i}untracked-function`}
|
||||
key={`untracked-function-${i}`}
|
||||
>
|
||||
<div
|
||||
className={`${styles.padd_right} ${
|
||||
styles.display_inline
|
||||
className={`${styles.display_inline} ${
|
||||
styles.add_mar_right
|
||||
}`}
|
||||
>
|
||||
<Button
|
||||
data-test={`view-function-${p.function_name}`}
|
||||
className={`${
|
||||
styles.display_inline
|
||||
} btn btn-xs btn-default`}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
|
||||
dispatch(_push('/data/sql'));
|
||||
|
||||
dispatch({
|
||||
type: SET_SQL,
|
||||
data: p.function_definition,
|
||||
});
|
||||
}}
|
||||
>
|
||||
View
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.display_inline}>
|
||||
{p.function_name}
|
||||
</div>
|
||||
</div>
|
||||
@ -642,7 +690,7 @@ class Schema extends Component {
|
||||
{getUntrackedTablesSection()}
|
||||
{getUntrackedRelationsSection()}
|
||||
{getUntrackedFunctionsSection()}
|
||||
{false && getNonTrackableFunctionsSection()}
|
||||
{getNonTrackableFunctionsSection()}
|
||||
<hr />
|
||||
{getPermissionsSummaryLink()}
|
||||
</div>
|
||||
|
@ -22,6 +22,7 @@ export const trackableFunctionsTip = (
|
||||
|
||||
export const nonTrackableFunctionsTip = (
|
||||
<Tooltip id="tooltip-functions-untrackable">
|
||||
Custom functions that do not conform to Hasura requirements
|
||||
Functions that do not conform to Hasura requirements to be exposed over the
|
||||
GraphQL API.
|
||||
</Tooltip>
|
||||
);
|
||||
|
@ -39,7 +39,11 @@ class EditItem extends Component {
|
||||
if (!oldItem) {
|
||||
dispatch(
|
||||
replace(
|
||||
`${globals.urlPrefix || ''}${getTableBrowseRoute(currentSchema, tableName, true)}`
|
||||
`${globals.urlPrefix || ''}${getTableBrowseRoute(
|
||||
currentSchema,
|
||||
tableName,
|
||||
true
|
||||
)}`
|
||||
)
|
||||
);
|
||||
return null;
|
||||
|
@ -470,14 +470,14 @@ const saveForeignKeys = (index, tableSchema, columns) => {
|
||||
alter table "${schemaName}"."${tableName}" drop constraint "${generatedConstraintName}",
|
||||
add constraint "${constraintName}"
|
||||
foreign key (${Object.keys(oldConstraint.column_mapping)
|
||||
.map(lc => `"${lc}"`)
|
||||
.join(', ')})
|
||||
.map(lc => `"${lc}"`)
|
||||
.join(', ')})
|
||||
references "${oldConstraint.ref_table_table_schema}"."${
|
||||
oldConstraint.ref_table
|
||||
}"
|
||||
oldConstraint.ref_table
|
||||
}"
|
||||
(${Object.values(oldConstraint.column_mapping)
|
||||
.map(rc => `"${rc}"`)
|
||||
.join(', ')})
|
||||
.map(rc => `"${rc}"`)
|
||||
.join(', ')})
|
||||
on update ${pgConfTypes[oldConstraint.on_update]}
|
||||
on delete ${pgConfTypes[oldConstraint.on_delete]};
|
||||
`;
|
||||
@ -725,8 +725,8 @@ const deleteTrigger = (trigger, table) => {
|
||||
|
||||
downMigrationSql += `CREATE TRIGGER "${triggerName}"
|
||||
${trigger.action_timing} ${
|
||||
trigger.event_manipulation
|
||||
} ON "${tableSchema}"."${tableName}"
|
||||
trigger.event_manipulation
|
||||
} ON "${tableSchema}"."${tableName}"
|
||||
FOR EACH ${trigger.action_orientation} ${trigger.action_statement};`;
|
||||
|
||||
if (trigger.comment) {
|
||||
@ -1586,24 +1586,24 @@ const saveColumnChangesSql = (colName, column, onSuccess) => {
|
||||
const schemaChangesUp =
|
||||
originalColType !== colType
|
||||
? [
|
||||
{
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
sql: columnChangesUpQuery,
|
||||
},
|
||||
{
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
sql: columnChangesUpQuery,
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
: [];
|
||||
const schemaChangesDown =
|
||||
originalColType !== colType
|
||||
? [
|
||||
{
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
sql: columnChangesDownQuery,
|
||||
},
|
||||
{
|
||||
type: 'run_sql',
|
||||
args: {
|
||||
sql: columnChangesDownQuery,
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
/* column custom field up/down migration*/
|
||||
|
Loading…
Reference in New Issue
Block a user