mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 08:02:15 +03:00
console: add comments to tracked functions
Closes https://github.com/hasura/graphql-engine/issues/7628 PR-URL: https://github.com/hasura/graphql-engine-mono/pull/2745 Co-authored-by: Vijay Prasanna <11921040+vijayprasanna13@users.noreply.github.com> Co-authored-by: Alberto Francesco Motta <36401353+afmotta@users.noreply.github.com> GitOrigin-RevId: 8a9c89a4175c5d0a48dada30a12cfdc0f4bf4250
This commit is contained in:
parent
e8023afaaf
commit
55c7be6b61
@ -2,8 +2,8 @@
|
||||
|
||||
## Next release
|
||||
(Add highlights/major features below)
|
||||
|
||||
- server: log locking DB queries during source catalog migration
|
||||
- console: add comments to tracked functions
|
||||
|
||||
### Bug fixes and improvements
|
||||
(Add entries below in the order of server, console, cli, docs, others)
|
||||
|
@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
|
||||
import { saveFunctionComment, setEditing } from '../customFunctionReducer';
|
||||
|
||||
interface FunctionCommentEditorProps {
|
||||
isEditing: boolean;
|
||||
defaultValue: string;
|
||||
readOnly: boolean;
|
||||
dispatch: (input: any) => void;
|
||||
}
|
||||
|
||||
export const FunctionCommentEditor: React.FC<FunctionCommentEditorProps> = ({
|
||||
isEditing,
|
||||
defaultValue,
|
||||
readOnly,
|
||||
dispatch,
|
||||
}) => {
|
||||
const [comment, setComment] = React.useState('');
|
||||
|
||||
const dispatchIsEditing = React.useCallback(
|
||||
(editing: boolean) => {
|
||||
dispatch(setEditing(editing));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const commentEditSave = () => {
|
||||
dispatch(saveFunctionComment(comment));
|
||||
};
|
||||
|
||||
const commentEditCancel = () => {
|
||||
setComment(defaultValue);
|
||||
dispatchIsEditing(false);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
setComment(defaultValue);
|
||||
}, [defaultValue]);
|
||||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<input
|
||||
onChange={event => setComment(event.target.value)}
|
||||
className="form-control"
|
||||
type="text"
|
||||
value={comment}
|
||||
placeholder="Function comment..."
|
||||
/>
|
||||
<div onClick={commentEditSave} className="ml-sm cursor-pointer">
|
||||
Save
|
||||
</div>
|
||||
<div onClick={commentEditCancel} className="ml-sm cursor-pointer">
|
||||
Cancel
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{comment && (
|
||||
<div className="rounded bg-secondary-light border border-gray-300 border-l-4 border-l-secondary py-sm px-md mb-sm">
|
||||
{comment}
|
||||
</div>
|
||||
)}
|
||||
{!readOnly && (
|
||||
<button
|
||||
onClick={() => dispatchIsEditing(true)}
|
||||
className="flex items-center cursor-pointer"
|
||||
>
|
||||
<i className="fa fa-edit mr-xs" />
|
||||
<span className="font-semibold">
|
||||
{comment ? 'Edit Comment' : 'Add a Comment'}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FunctionCommentEditor;
|
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import Helmet from 'react-helmet';
|
||||
@ -9,6 +10,7 @@ import globals from '../../../../../Globals';
|
||||
import Button from '../../../../Common/Button/Button';
|
||||
import styles from './ModifyCustomFunction.scss';
|
||||
import TextAreaWithCopy from '../../../../Common/TextAreaWithCopy/TextAreaWithCopy';
|
||||
import FunctionCommentEditor from './FunctionCommentEditor';
|
||||
import {
|
||||
fetchCustomFunction,
|
||||
unTrackCustomFunction,
|
||||
@ -23,7 +25,8 @@ import {
|
||||
} from '../../../../Common/utils/routesUtils';
|
||||
import SessionVarSection from './SessionVarSection';
|
||||
import RawSqlButton from '../../Common/Components/RawSqlButton';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { isFeatureSupported } from '@/dataSources';
|
||||
|
||||
export const pageTitle = 'Custom Function';
|
||||
|
||||
@ -122,6 +125,8 @@ class ModifyCustomFunction extends React.Component {
|
||||
functionSchema: schema,
|
||||
functionName,
|
||||
functionDefinition,
|
||||
functionComment,
|
||||
isEditingComment,
|
||||
isRequesting,
|
||||
isDeleting,
|
||||
isUntracking,
|
||||
@ -220,7 +225,24 @@ class ModifyCustomFunction extends React.Component {
|
||||
showLoader={isFetching}
|
||||
testPrefix={'functions'}
|
||||
/>
|
||||
|
||||
<br />
|
||||
{isFeatureSupported('functions.modify.comments.view') && (
|
||||
<div className="w-full sm:w-6/12 mb-lg">
|
||||
<h4 className="flex items-center text-gray-600 font-semibold mb-formlabel">
|
||||
Function Comments
|
||||
</h4>
|
||||
|
||||
<FunctionCommentEditor
|
||||
isEditing={isEditingComment}
|
||||
defaultValue={functionComment}
|
||||
dispatch={dispatch}
|
||||
readOnly={!isFeatureSupported('functions.modify.comments.edit')}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="w-full sm:w-6/12 mb-md">
|
||||
<h4 className="flex items-center text-gray-600 font-semibold mb-formlabel">
|
||||
Function Definition:
|
||||
|
@ -1,3 +1,4 @@
|
||||
import Migration from '@/utils/migration/Migration';
|
||||
import { dataSource } from '../../../../dataSources';
|
||||
import Endpoints, { globalCookiePolicy } from '../../../../Endpoints';
|
||||
import { exportMetadata } from '../../../../metadata/actions';
|
||||
@ -47,6 +48,12 @@ const PERMISSION_CUSTOM_FUNCTION_DROP_SUCCESS =
|
||||
const PERMISSION_CUSTOM_FUNCTION_DROP_FAIL =
|
||||
'@customFunction/PERMISSION_CUSTOM_FUNCTION_DROP_FAIL';
|
||||
|
||||
const FUNCTION_COMMENT_SET_SUCCESS =
|
||||
'@customFunction/FUNCTION_COMMENT_SET_SUCCESS';
|
||||
const CUSTOM_FUNCTION_COMMENT_EDITING =
|
||||
'@customFunction/CUSTOM_FUNCTION_COMMENT_EDITING';
|
||||
const FUNCTION_COMMENT_SET_FAIL = '@customFunction/FUNCTION_COMMENT_SET_FAIL';
|
||||
|
||||
/* Action creators */
|
||||
const fetchCustomFunction = (functionName, schema, source) => {
|
||||
return (dispatch, getState) => {
|
||||
@ -262,6 +269,69 @@ const updateSessVar = session_argument => {
|
||||
};
|
||||
};
|
||||
|
||||
const setEditing = editing => dispatch =>
|
||||
dispatch({ type: CUSTOM_FUNCTION_COMMENT_EDITING, data: editing });
|
||||
|
||||
const saveFunctionComment = updatedComment => (dispatch, getState) => {
|
||||
const source = getState().tables.currentDataSource;
|
||||
const { currentSchema } = getState().tables;
|
||||
const { functionName } = getState().functions;
|
||||
|
||||
const upQuery = dataSource.getSetCommentSql(
|
||||
'function',
|
||||
'',
|
||||
currentSchema,
|
||||
updatedComment || null,
|
||||
null,
|
||||
functionName
|
||||
);
|
||||
|
||||
const downQuery = dataSource.getSetCommentSql(
|
||||
'function',
|
||||
'',
|
||||
currentSchema,
|
||||
'NULL',
|
||||
null,
|
||||
functionName
|
||||
);
|
||||
|
||||
const migration = new Migration();
|
||||
migration.add(
|
||||
getRunSqlQuery(upQuery, source),
|
||||
getRunSqlQuery(downQuery, source)
|
||||
);
|
||||
|
||||
const migrationName =
|
||||
'alter_function_' + currentSchema + '_' + functionName + '_update_comment';
|
||||
const requestMsg = 'Updating Comment...';
|
||||
const successMsg = 'Comment Updated';
|
||||
const errorMsg = 'Updating comment failed';
|
||||
|
||||
const customOnSuccess = () => {
|
||||
dispatch({
|
||||
type: FUNCTION_COMMENT_SET_SUCCESS,
|
||||
data: { functionComment: updatedComment },
|
||||
});
|
||||
};
|
||||
|
||||
const customOnError = err => {
|
||||
dispatch({ type: FUNCTION_COMMENT_SET_FAIL, data: err });
|
||||
};
|
||||
|
||||
return dispatch(
|
||||
makeRequest(
|
||||
migration.upMigration,
|
||||
migration.downMigration,
|
||||
migrationName,
|
||||
customOnSuccess,
|
||||
customOnError,
|
||||
requestMsg,
|
||||
successMsg,
|
||||
errorMsg
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const setFunctionPermission = (userRole, onSuccessCb) => (
|
||||
dispatch,
|
||||
getState
|
||||
@ -377,6 +447,7 @@ const customFunctionReducer = (state = functionData, action) => {
|
||||
setOffTableSchema: action?.data?.return_type_schema || null,
|
||||
inputArgNames: action?.data?.input_arg_names || null,
|
||||
inputArgTypes: action?.data?.input_arg_types || null,
|
||||
functionComment: action?.data?.comment || '',
|
||||
isFetching: false,
|
||||
isUpdating: false,
|
||||
isFetchError: null,
|
||||
@ -453,6 +524,16 @@ const customFunctionReducer = (state = functionData, action) => {
|
||||
isPermissionDrop: false,
|
||||
isError: action.data,
|
||||
};
|
||||
case CUSTOM_FUNCTION_COMMENT_EDITING:
|
||||
return { ...state, isEditingComment: action?.data };
|
||||
case FUNCTION_COMMENT_SET_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
isEditingComment: false,
|
||||
functionComment: action?.data?.functionComment,
|
||||
};
|
||||
case FUNCTION_COMMENT_SET_FAIL:
|
||||
return { ...state, isEditingComment: false, isError: action?.data };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
@ -461,7 +542,9 @@ const customFunctionReducer = (state = functionData, action) => {
|
||||
export {
|
||||
deleteFunction,
|
||||
dropFunctionPermission,
|
||||
saveFunctionComment,
|
||||
fetchCustomFunction,
|
||||
setEditing,
|
||||
RESET,
|
||||
setFunctionPermission,
|
||||
unTrackCustomFunction,
|
||||
|
@ -14,6 +14,8 @@ const functionData = {
|
||||
functionName: '',
|
||||
functionSchema: '',
|
||||
functionDefinition: '',
|
||||
functionComment: '',
|
||||
isEditingComment: false,
|
||||
configuration: {},
|
||||
permissions: {},
|
||||
setOffTable: '',
|
||||
|
@ -170,6 +170,13 @@ export const supportedFeatures: DeepRequired<SupportedFeaturesType> = {
|
||||
nonTrackableFunctions: {
|
||||
enabled: false,
|
||||
},
|
||||
modify: {
|
||||
enabled: false,
|
||||
comments: {
|
||||
view: false,
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
events: {
|
||||
triggers: {
|
||||
|
@ -68,6 +68,13 @@ export const supportedFeatures: DeepRequired<SupportedFeaturesType> = {
|
||||
nonTrackableFunctions: {
|
||||
enabled: true,
|
||||
},
|
||||
modify: {
|
||||
enabled: true,
|
||||
comments: {
|
||||
view: true,
|
||||
edit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -188,6 +188,13 @@ export const supportedFeatures: DeepRequired<SupportedFeaturesType> = {
|
||||
nonTrackableFunctions: {
|
||||
enabled: false,
|
||||
},
|
||||
modify: {
|
||||
enabled: false,
|
||||
comments: {
|
||||
view: false,
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
events: {
|
||||
triggers: {
|
||||
|
@ -647,6 +647,13 @@ export const supportedFeatures: DeepRequired<SupportedFeaturesType> = {
|
||||
nonTrackableFunctions: {
|
||||
enabled: true,
|
||||
},
|
||||
modify: {
|
||||
enabled: true,
|
||||
comments: {
|
||||
view: true,
|
||||
edit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
events: {
|
||||
triggers: {
|
||||
|
@ -663,12 +663,21 @@ export const getSetColumnDefaultSql = (
|
||||
};
|
||||
|
||||
export const getSetCommentSql = (
|
||||
on: 'column' | 'table' | string,
|
||||
on: string,
|
||||
tableName: string,
|
||||
schemaName: string,
|
||||
comment: string | null,
|
||||
columnName?: string
|
||||
columnName?: string,
|
||||
functionName?: string
|
||||
) => {
|
||||
if (functionName) {
|
||||
return `
|
||||
comment on ${on} "${schemaName}"."${functionName}" is ${
|
||||
comment ? sqlEscapeText(comment) : 'NULL'
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
if (columnName) {
|
||||
return `
|
||||
comment on ${on} "${schemaName}"."${tableName}"."${columnName}" is ${
|
||||
@ -857,6 +866,7 @@ pg_get_functiondef(p.oid) AS function_definition,
|
||||
rtn.nspname::text AS return_type_schema,
|
||||
rt.typname::text AS return_type_name,
|
||||
rt.typtype::text AS return_type_type,
|
||||
obj_description(p.oid) AS comment,
|
||||
p.proretset AS returns_set,
|
||||
( SELECT COALESCE(json_agg(json_build_object('schema', q.schema, 'name', q.name, 'type', q.type)), '[]'::json) AS "coalesce"
|
||||
FROM ( SELECT pt.typname AS name,
|
||||
|
@ -323,6 +323,13 @@ export type SupportedFeaturesType = {
|
||||
nonTrackableFunctions: {
|
||||
enabled: boolean;
|
||||
};
|
||||
modify: {
|
||||
enabled: boolean;
|
||||
comments: {
|
||||
view: boolean;
|
||||
edit: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
events: {
|
||||
triggers: {
|
||||
|
Loading…
Reference in New Issue
Block a user