mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-09-20 06:58:39 +03:00
This commit is contained in:
parent
bc16668ff6
commit
b2fd57a8e2
11
CHANGELOG.md
11
CHANGELOG.md
@ -12,23 +12,20 @@ It works similar to table relationships. Head to the `Relationship` tab in your
|
||||
2. select the remote schema
|
||||
3. give the join configuration from table columns to remote schema fields.
|
||||
|
||||
[Add docs links]
|
||||
[Add console screenshot]
|
||||
[Add docs links][add console screenshot]
|
||||
|
||||
### Scheduled Triggers
|
||||
|
||||
A scheduled trigger can be used to execute custom business logic based on time. There are two types of timing events: cron based or timestamp based.
|
||||
|
||||
A cron trigger will be useful when something needs to be done periodically. For example, you can create a cron trigger to generate an end-of-day sales report every weekday at 9pm.
|
||||
A cron trigger will be useful when something needs to be done periodically. For example, you can create a cron trigger to generate an end-of-day sales report every weekday at 9pm.
|
||||
|
||||
You can also schedule one-off events based on a timestamp. For example, a new scheduled event can be created for 2 weeks from when a user signs up to send them an email about their experience.
|
||||
|
||||
[Add docs links]
|
||||
[Add console screenshot]
|
||||
[Add docs links][add console screenshot]
|
||||
|
||||
(close #1914)
|
||||
|
||||
|
||||
### Allow access to session variables by computed fields (fix #3846)
|
||||
|
||||
Sometimes it is useful for computed fields to have access to the Hasura session variables directly. For example, suppose you want to fetch some articles but also get related user info, say `likedByMe`. Now, you can define a function like:
|
||||
@ -63,6 +60,7 @@ Read more about the session argument for computed fields in the [docs](https://h
|
||||
### Bug fixes and improvements
|
||||
|
||||
(Add entries here in the order of: server, console, cli, docs, others)
|
||||
|
||||
- server: fix explain queries with role permissions (fix #4816)
|
||||
- server: compile with GHC 8.10.1, closing a space leak with subscriptions. (close #4517) (#3388)
|
||||
|
||||
@ -81,6 +79,7 @@ Read more about the session argument for computed fields in the [docs](https://h
|
||||
- console: fix inconsistency between selected rows state and displayed rows (fix #4654) (#4673)
|
||||
- console: fix displaying boolean values in `Edit Row` tab (#4682)
|
||||
- console: fix underscores not being displayed on raw sql page (close #4754) (#4799)
|
||||
- console: fix visiting view modify page overwriting raw sql content (fix #4798) (#4810)
|
||||
- console: add help button and move about page to settings (#4848)
|
||||
- cli: list all available commands in root command help (fix #4623) (#4628)
|
||||
- docs: add section on actions vs. remote schemas to actions documentation (#4284)
|
||||
|
@ -14,63 +14,67 @@ export const openRawSQL = () => {
|
||||
// Match URL
|
||||
cy.url().should('eq', `${baseUrl}/data/sql`);
|
||||
};
|
||||
|
||||
const clearText = () => {
|
||||
cy.get('textarea').type('{selectall}', { force: true });
|
||||
cy.get('textarea').trigger('keydown', {
|
||||
keyCode: 46,
|
||||
which: 46,
|
||||
force: true,
|
||||
});
|
||||
cy.wait(2000); // ace editor textarea doesn't expose the value to check, so wait
|
||||
};
|
||||
export const passCreateTable = () => {
|
||||
prevStr = 'CREATE TABLE Apic_test_table_rsql (id serial PRIMARY KEY);';
|
||||
cy.get('textarea').type(prevStr, { force: true });
|
||||
cy.wait(1000); // debounce
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const passInsertValues = () => {
|
||||
for (let i = 0; i < prevStr.length; i++) {
|
||||
cy.get('textarea').type('{backspace}', { force: true });
|
||||
}
|
||||
clearText();
|
||||
prevStr = 'INSERT INTO Apic_test_table_rsql VALUES (1);';
|
||||
cy.get('textarea').type(prevStr, { force: true });
|
||||
cy.wait(1000);
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const passAlterTable = () => {
|
||||
for (let i = 0; i < prevStr.length; i++) {
|
||||
cy.get('textarea').type('{backspace}', { force: true });
|
||||
}
|
||||
clearText();
|
||||
prevStr = 'ALTER TABLE Apic_test_table_rsql ADD COLUMN name text;';
|
||||
cy.get('textarea').type(prevStr, { force: true });
|
||||
// Untrack table
|
||||
cy.wait(1000);
|
||||
cy.get(getElementFromAlias('raw-sql-track-check')).uncheck();
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const passCreateView = () => {
|
||||
for (let i = 0; i < prevStr.length; i++) {
|
||||
cy.get('textarea').type('{backspace}', { force: true });
|
||||
}
|
||||
clearText();
|
||||
prevStr = 'CREATE VIEW abcd AS SELECT * FROM Apic_test_table_rsql;';
|
||||
cy.get('textarea').type(prevStr, { force: true });
|
||||
// Track table
|
||||
cy.wait(1000);
|
||||
cy.get(getElementFromAlias('raw-sql-track-check')).check();
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
cy.wait(5000);
|
||||
};
|
||||
|
||||
export const delTestTables = () => {
|
||||
for (let i = 0; i < prevStr.length; i++) {
|
||||
cy.get('textarea').type('{backspace}', { force: true });
|
||||
}
|
||||
clearText();
|
||||
prevStr = 'DROP TABLE Apic_test_table_rsql CASCADE;';
|
||||
cy.get('textarea').type(prevStr, { force: true });
|
||||
cy.wait(1000);
|
||||
cy.get(getElementFromAlias('raw-sql-migration-check')).uncheck();
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
cy.get(getElementFromAlias('not-migration-confirm')).click();
|
||||
cy.wait(5000);
|
||||
for (let i = 0; i < prevStr.length; i++) {
|
||||
cy.get('textarea').type('{backspace}', { force: true });
|
||||
}
|
||||
prevStr = 'DROP TABLE abcd;';
|
||||
cy.get('textarea').type(prevStr, { force: true });
|
||||
cy.get(getElementFromAlias('run-sql')).click();
|
||||
cy.wait(5000);
|
||||
// cy.visit(`${baseUrl}/data/schema/public`);
|
||||
// cy.get(getElementFromAlias('add-track-table-Apic_test_table_rsql')).click();
|
||||
// cy.get(getElementFromAlias('delete-table')).click();
|
||||
// cy.on('window:confirm', () => true);
|
||||
// cy.wait(5000);
|
||||
// validateCT('Apic_test_table_rsql', 'failure');
|
||||
};
|
||||
|
16
console/src/components/Common/Alert/index.tsx
Normal file
16
console/src/components/Common/Alert/index.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
|
||||
export type AlertType = 'warning' | 'danger' | 'success';
|
||||
|
||||
interface AlertProps {
|
||||
type: AlertType;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const Alert: React.FC<AlertProps> = ({ type, text }) => (
|
||||
<div className={`hidden alert alert-${type}`} role="alert">
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Alert;
|
@ -12,7 +12,7 @@ import styles from '../Common.scss';
|
||||
|
||||
export interface ButtonProps extends React.ComponentProps<'button'> {
|
||||
size: string;
|
||||
color: 'yellow' | 'red' | 'green' | 'gray' | 'white' | 'black';
|
||||
color?: 'yellow' | 'red' | 'green' | 'gray' | 'white' | 'black';
|
||||
}
|
||||
|
||||
const Button: React.FC<ButtonProps> = props => {
|
||||
|
@ -142,6 +142,7 @@ const defaultModifyState = {
|
||||
lastSuccess: null,
|
||||
viewDefinition: null,
|
||||
viewDefinitionError: null,
|
||||
viewDefSql: '',
|
||||
tableCommentEdit: { enabled: false, editedValue: null },
|
||||
alterColumnOptions: [], // Store supported implicit column -> column casts
|
||||
alterColumnOptionsFetchErr: null,
|
||||
|
@ -24,7 +24,31 @@ import {
|
||||
ACE_EDITOR_FONT_SIZE,
|
||||
} from '../../../Common/AceEditor/utils';
|
||||
import { CLI_CONSOLE_MODE } from '../../../../constants';
|
||||
import NotesSection from './molecules/NotesSection';
|
||||
import Alert from '../../../Common/Alert';
|
||||
|
||||
/**
|
||||
* # RawSQL React FC
|
||||
* ## renders raw SQL page on route `/data/sql`
|
||||
*
|
||||
* @typedef Props
|
||||
* @property {string} sql
|
||||
* @property {string} resultType
|
||||
* @property {array} result
|
||||
* @property {array} resultHeaders
|
||||
* @property {function} dispatch
|
||||
* @property {boolean} ongoingRequest
|
||||
* @property {object} lastError
|
||||
* @property {boolean} lastSuccess
|
||||
* @property {boolean} isModalOpen
|
||||
* @property {boolean} isCascadeChecked
|
||||
* @property {boolean} isMigrationChecked
|
||||
* @property {boolean} isTableTrackChecked
|
||||
* @property {boolean} migrationMode
|
||||
* @property {array} allSchemas
|
||||
*
|
||||
* @param {Props}
|
||||
*/
|
||||
const RawSQL = ({
|
||||
sql,
|
||||
resultType,
|
||||
@ -51,7 +75,6 @@ const RawSQL = ({
|
||||
// set up sqlRef to use in unmount
|
||||
const sqlRef = useRef(sql);
|
||||
|
||||
// set SQL from localStorage on mount and write back to localStorage on unmount
|
||||
useEffect(() => {
|
||||
if (!sql) {
|
||||
const sqlFromLocalStorage = localStorage.getItem(LS_RAW_SQL_SQL);
|
||||
@ -59,12 +82,10 @@ const RawSQL = ({
|
||||
dispatch({ type: SET_SQL, data: sqlFromLocalStorage });
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
localStorage.setItem(LS_RAW_SQL_SQL, sqlRef.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// set SQL to sqlRef
|
||||
useEffect(() => {
|
||||
sqlRef.current = sql;
|
||||
@ -127,34 +148,6 @@ const RawSQL = ({
|
||||
}
|
||||
};
|
||||
|
||||
let alert = null;
|
||||
|
||||
if (ongoingRequest) {
|
||||
alert = (
|
||||
<div className={`${styles.padd_left_remove} col-xs-12`}>
|
||||
<div className="hidden alert alert-warning" role="alert">
|
||||
Running...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (lastError) {
|
||||
alert = (
|
||||
<div className={`${styles.padd_left_remove} col-xs-12`}>
|
||||
<div className="hidden alert alert-danger" role="alert">
|
||||
Error: {JSON.stringify(lastError)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (lastSuccess) {
|
||||
alert = (
|
||||
<div className={`${styles.padd_left_remove} col-xs-12`}>
|
||||
<div className="hidden alert alert-success" role="alert">
|
||||
Executed Query
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const getMigrationWarningModal = () => {
|
||||
const onModalClose = () => {
|
||||
dispatch(modalClose());
|
||||
@ -256,6 +249,8 @@ const RawSQL = ({
|
||||
},
|
||||
]}
|
||||
onChange={handleSQLChange}
|
||||
// prevents unwanted frequent event triggers
|
||||
debounceChangePeriod={200}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@ -305,25 +300,6 @@ const RawSQL = ({
|
||||
return resultTable;
|
||||
};
|
||||
|
||||
const getNotesSection = () => {
|
||||
return (
|
||||
<ul>
|
||||
<li>
|
||||
You can create views, alter tables or just about run any SQL
|
||||
statements directly on the database.
|
||||
</li>
|
||||
<li>
|
||||
Multiple SQL statements can be separated by semicolons, <code>;</code>
|
||||
, however, only the result of the last SQL statement will be returned.
|
||||
</li>
|
||||
<li>
|
||||
Multiple SQL statements will be run as a transaction. i.e. if any
|
||||
statement fails, none of the statements will be applied.
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
const getMetadataCascadeSection = () => {
|
||||
return (
|
||||
<div className={styles.add_mar_top_small}>
|
||||
@ -426,15 +402,7 @@ const RawSQL = ({
|
||||
<div>
|
||||
<label className={styles.add_mar_right}>Migration name:</label>
|
||||
<input
|
||||
className={
|
||||
styles.inline_block +
|
||||
' ' +
|
||||
styles.tableNameInput +
|
||||
' ' +
|
||||
styles.add_mar_right_small +
|
||||
' ' +
|
||||
' form-control'
|
||||
}
|
||||
className={`${styles.inline_block} ${styles.tableNameInput} ${styles.add_mar_right_small} form-control`}
|
||||
placeholder={'run_sql_migration'}
|
||||
id="migration-name"
|
||||
type="text"
|
||||
@ -473,21 +441,6 @@ const RawSQL = ({
|
||||
return migrationSection;
|
||||
};
|
||||
|
||||
const getRunButton = () => {
|
||||
return (
|
||||
<Button
|
||||
type="submit"
|
||||
className={styles.add_mar_top}
|
||||
onClick={submitSQL}
|
||||
color="yellow"
|
||||
size="sm"
|
||||
data-test="run-sql"
|
||||
>
|
||||
Run!
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${styles.clear_fix} ${styles.padd_left} ${styles.padd_top}`}
|
||||
@ -500,26 +453,44 @@ const RawSQL = ({
|
||||
<div className="clearfix" />
|
||||
</div>
|
||||
<div className={styles.add_mar_top}>
|
||||
<div>
|
||||
<div className={`${styles.padd_left_remove} col-xs-8`}>
|
||||
{getNotesSection()}
|
||||
</div>
|
||||
<div className={`${styles.padd_left_remove} col-xs-8`}>
|
||||
<NotesSection />
|
||||
</div>
|
||||
|
||||
<div className={`${styles.padd_left_remove} col-xs-10`}>
|
||||
{getSQLSection()}
|
||||
</div>
|
||||
<div className={`${styles.padd_left_remove} col-xs-10`}>
|
||||
{getSQLSection()}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`${styles.padd_left_remove} ${styles.add_mar_bottom} col-xs-8`}
|
||||
<div
|
||||
className={`${styles.padd_left_remove} ${styles.add_mar_bottom} col-xs-8`}
|
||||
>
|
||||
{getTrackThisSection()}
|
||||
{getMetadataCascadeSection()}
|
||||
{getMigrationSection()}
|
||||
<Button
|
||||
type="submit"
|
||||
className={styles.add_mar_top}
|
||||
onClick={submitSQL}
|
||||
color="yellow"
|
||||
size="sm"
|
||||
data-test="run-sql"
|
||||
>
|
||||
{getTrackThisSection()}
|
||||
{getMetadataCascadeSection()}
|
||||
{getMigrationSection()}
|
||||
Run!
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{getRunButton()}
|
||||
<div className="hidden col-xs-4">
|
||||
<div className={`${styles.padd_left_remove} col-xs-12`}>
|
||||
{ongoingRequest && <Alert type="warning" text="Running..." />}
|
||||
{lastError && (
|
||||
<Alert
|
||||
type="danger"
|
||||
text={`Error: ${JSON.stringify(lastError)}`}
|
||||
/>
|
||||
)}
|
||||
{lastSuccess && <Alert type="success" text="Executed Query" />};
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden col-xs-4">{alert}</div>
|
||||
</div>
|
||||
|
||||
{getMigrationWarningModal()}
|
||||
|
@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
|
||||
const NotesSection = () => {
|
||||
return (
|
||||
<ul>
|
||||
<li>
|
||||
You can create views, alter tables or just about run any SQL statements
|
||||
directly on the database.
|
||||
</li>
|
||||
<li>
|
||||
Multiple SQL statements can be separated by semicolons, <code>;</code>,
|
||||
however, only the result of the last SQL statement will be returned.
|
||||
</li>
|
||||
<li>
|
||||
Multiple SQL statements will be run as a transaction. i.e. if any
|
||||
statement fails, none of the statements will be applied.
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotesSection;
|
@ -23,6 +23,7 @@ import {
|
||||
TOGGLE_ENUM_FAILURE,
|
||||
MODIFY_ROOT_FIELD,
|
||||
SET_CHECK_CONSTRAINTS,
|
||||
SET_VIEW_DEF_SQL,
|
||||
} from '../TableModify/ModifyActions';
|
||||
|
||||
// TABLE RELATIONSHIPS
|
||||
@ -119,6 +120,11 @@ const modifyReducer = (tableName, schemas, modifyStateOrig, action) => {
|
||||
...modifyState,
|
||||
viewDefinitionError: action.data,
|
||||
};
|
||||
case SET_VIEW_DEF_SQL:
|
||||
return {
|
||||
...modifyState,
|
||||
viewDefSql: action.data,
|
||||
};
|
||||
|
||||
case REL_ADD_NEW_CLICKED:
|
||||
return {
|
||||
|
@ -8,7 +8,6 @@ import {
|
||||
LOAD_SCHEMA,
|
||||
} from '../DataActions';
|
||||
import _push from '../push';
|
||||
import { SET_SQL } from '../RawSQL/Actions';
|
||||
import {
|
||||
showErrorNotification,
|
||||
showSuccessNotification,
|
||||
@ -73,6 +72,7 @@ const DELETE_PK_WARNING =
|
||||
|
||||
const VIEW_DEF_REQUEST_SUCCESS = 'ModifyTable/VIEW_DEF_REQUEST_SUCCESS';
|
||||
const VIEW_DEF_REQUEST_ERROR = 'ModifyTable/VIEW_DEF_REQUEST_ERROR';
|
||||
const SET_VIEW_DEF_SQL = 'ModifyTable/SET_VIEW_DEF_SQL';
|
||||
|
||||
const SAVE_NEW_TABLE_NAME = 'ModifyTable/SAVE_NEW_TABLE_NAME';
|
||||
|
||||
@ -967,7 +967,7 @@ WHERE c.relname = '${viewName}'
|
||||
' AS \n' +
|
||||
finalDef;
|
||||
}
|
||||
dispatch({ type: SET_SQL, data: runSqlDef });
|
||||
dispatch({ type: SET_VIEW_DEF_SQL, data: runSqlDef });
|
||||
},
|
||||
err => {
|
||||
dispatch(
|
||||
@ -2369,6 +2369,7 @@ export {
|
||||
FETCH_COLUMN_TYPE_CASTS_FAIL,
|
||||
VIEW_DEF_REQUEST_SUCCESS,
|
||||
VIEW_DEF_REQUEST_ERROR,
|
||||
SET_VIEW_DEF_SQL,
|
||||
SET_COLUMN_EDIT,
|
||||
TABLE_COMMENT_EDIT,
|
||||
TABLE_COMMENT_INPUT_EDIT,
|
||||
|
@ -1,6 +1,5 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import AceEditor from 'react-ace';
|
||||
import TableHeader from '../TableCommon/TableHeader';
|
||||
import ExpandableEditor from '../../../Common/Layout/ExpandableEditor/Editor';
|
||||
import {
|
||||
@ -28,10 +27,11 @@ import RootFields from './RootFields';
|
||||
import Tooltip from '../../../Common/Tooltip/Tooltip';
|
||||
import { changeViewRootFields } from '../Common/TooltipMessages';
|
||||
import styles from './ModifyTable.scss';
|
||||
import ViewDefinitions from './ViewDefinitions';
|
||||
|
||||
const ModifyView = props => {
|
||||
const {
|
||||
sql,
|
||||
viewDefSql,
|
||||
tableName,
|
||||
tableType,
|
||||
allSchemas,
|
||||
@ -98,12 +98,6 @@ const ModifyView = props => {
|
||||
);
|
||||
}
|
||||
|
||||
const modifyViewDefinition = viewName => {
|
||||
// fetch the definition
|
||||
dispatch(fetchViewDefinition(viewName, true));
|
||||
// redirect the user to run_sql page and set state
|
||||
};
|
||||
|
||||
const getViewColumnsSection = () => {
|
||||
const columns = tableSchema.columns.sort(ordinalColSort);
|
||||
|
||||
@ -200,21 +194,6 @@ const ModifyView = props => {
|
||||
);
|
||||
};
|
||||
|
||||
const modifyViewOnClick = () => {
|
||||
modifyViewDefinition(tableName);
|
||||
};
|
||||
const modifyBtn = (
|
||||
<Button
|
||||
type="submit"
|
||||
size="xs"
|
||||
className={styles.add_mar_right}
|
||||
onClick={modifyViewOnClick}
|
||||
data-test="modify-view"
|
||||
>
|
||||
Modify
|
||||
</Button>
|
||||
);
|
||||
|
||||
const untrackOnclick = () => {
|
||||
const confirmMessage = `This will remove the view "${tableName}" from the GraphQL schema`;
|
||||
const isOk = getConfirmation(confirmMessage);
|
||||
@ -276,21 +255,8 @@ const ModifyView = props => {
|
||||
<h4 className={styles.subheading_text}>Columns</h4>
|
||||
{getViewColumnsSection()}
|
||||
<br />
|
||||
<h4 className={styles.subheading_text}>
|
||||
View Definition:
|
||||
<span className={styles.add_mar_left}>{modifyBtn}</span>
|
||||
</h4>
|
||||
<AceEditor
|
||||
mode="sql"
|
||||
theme="github"
|
||||
value={sql}
|
||||
name="raw_sql"
|
||||
minLines={8}
|
||||
maxLines={100}
|
||||
width="100%"
|
||||
showPrintMargin={false}
|
||||
readOnly
|
||||
/>
|
||||
<ViewDefinitions dispatch={dispatch} sql={viewDefSql} />
|
||||
|
||||
<hr />
|
||||
{getViewRootFieldsSection()}
|
||||
{untrackBtn}
|
||||
@ -342,7 +308,6 @@ const mapStateToProps = (state, ownProps) => {
|
||||
tableType,
|
||||
currentSchema: schemaName,
|
||||
allSchemas: state.tables.allSchemas,
|
||||
sql: state.rawSQL.sql,
|
||||
migrationMode: state.main.migrationMode,
|
||||
readOnlyMode: state.main.readOnlyMode,
|
||||
serverVersion: state.main.serverVersion,
|
||||
|
@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
import styles from './ModifyTable.scss';
|
||||
import TextAreaWithCopy from '../../../Common/TextAreaWithCopy/TextAreaWithCopy';
|
||||
import RawSqlButton from '../Common/Components/RawSqlButton';
|
||||
|
||||
export interface ViewDefinitionsProps {
|
||||
dispatch: () => void;
|
||||
sql: string | object;
|
||||
}
|
||||
|
||||
const ViewDefinitions: React.FC<ViewDefinitionsProps> = ({ dispatch, sql }) => (
|
||||
<>
|
||||
<h4 className={styles.subheading_text}>
|
||||
View Definition:
|
||||
<span className={styles.add_mar_left}>
|
||||
<RawSqlButton
|
||||
className={styles.add_mar_right}
|
||||
sql={sql}
|
||||
dispatch={dispatch}
|
||||
data-test="modify-view"
|
||||
>
|
||||
Modify
|
||||
</RawSqlButton>
|
||||
</span>
|
||||
</h4>
|
||||
|
||||
<TextAreaWithCopy
|
||||
copyText={sql}
|
||||
textLanguage="sql"
|
||||
id="copyCustomFunctionSQL"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
export default ViewDefinitions;
|
@ -21,9 +21,11 @@ export const LoadingSkeleton = () => {
|
||||
);
|
||||
return (
|
||||
<div className={`${styles.schemaExplorerContainer} ${styles.overflowAuto}`}>
|
||||
{Array(5).fill(null).map((_, i) => (
|
||||
<div key={i}>{skeletonItem}</div>
|
||||
))}
|
||||
{Array(5)
|
||||
.fill(null)
|
||||
.map((_, i) => (
|
||||
<div key={i}>{skeletonItem}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user