diff --git a/CHANGELOG.md b/CHANGELOG.md index c1e8e3531b8..0e688e4c260 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - server: fix to allow remote schema response to contain an "extensions" field (#7143) - server: support database-to-database joins with BigQuery - console: add comments to tracked functions +- console: add select all columns option while selecting the columns in event triggers - console: add request transforms for events - metadata SDK: add type definitions for config v3 - cli: fix cli-console failing to add migrations if there are tabs in SQL body (#7362) diff --git a/console/cypress/integration/events/create-trigger/spec.ts b/console/cypress/integration/events/create-trigger/spec.ts index 24142a2160a..2420888410c 100644 --- a/console/cypress/integration/events/create-trigger/spec.ts +++ b/console/cypress/integration/events/create-trigger/spec.ts @@ -15,9 +15,9 @@ import { import { setMetaData, validateCT, - validateCTrigger, validateInsert, ResultType, + validateCTrigger, } from '../../validators/validators'; import { setPromptValue } from '../../../helpers/common'; import { @@ -55,7 +55,7 @@ const statements = { `, }; -const createETForm = () => { +const createETForm = (allCols: boolean) => { // Set trigger name and select table cy.getBySel('trigger-name').clear().type(getTriggerName(0, testName)); cy.getBySel('select-source').select('default'); @@ -73,6 +73,10 @@ const createETForm = () => { // advanced settings cy.getBySel('advanced-settings').click(); + if (!allCols) { + cy.getBySel('choose-column').click(); + cy.getBySel('select-column').first().click(); + } // retry configuration cy.getBySel('no-of-retries').clear().type(getNoOfRetries()); @@ -139,8 +143,10 @@ export const failCTWithoutData = () => { validateCT(getTriggerName(0, testName), ResultType.FAILURE); }; -export const passCT = () => { - createETForm(); +export const passCT1 = () => { + // select choose column from the radio input + const allCols = false; + createETForm(allCols); // Click on create cy.getBySel('trigger-create').click(); @@ -159,7 +165,36 @@ export const passCT = () => { getTriggerName(0, testName), getTableName(0, testName), 'public', - ResultType.SUCCESS + ResultType.SUCCESS, + allCols + ); +}; + +export const passCT2 = () => { + cy.getBySel('data-sidebar-add').click(); + // select all columns from the radio input + const allCols = true; + createETForm(allCols); + + // Click on create + cy.getBySel('trigger-create').click(); + cy.wait(10000); + // Check if the trigger got created and navigated to processed events page + cy.url().should( + 'eq', + `${baseUrl}${EVENT_TRIGGER_INDEX_ROUTE}/${getTriggerName( + 0, + testName + )}/modify` + ); + cy.getBySel(getTriggerName(0, testName)); + // Validate + validateCTrigger( + getTriggerName(0, testName), + getTableName(0, testName), + 'public', + ResultType.SUCCESS, + allCols ); }; @@ -272,7 +307,7 @@ export const createEtTransform = () => { cy.url().should('eq', `${baseUrl}${EVENT_TRIGGER_INDEX_ROUTE}/add`); // fill up the basic event trigger form - createETForm(); + createETForm(true); // open request transform section toggleRequestTransformSection(); diff --git a/console/cypress/integration/events/create-trigger/test.ts b/console/cypress/integration/events/create-trigger/test.ts index 2ab08edec37..d14d3394bcd 100644 --- a/console/cypress/integration/events/create-trigger/test.ts +++ b/console/cypress/integration/events/create-trigger/test.ts @@ -6,12 +6,13 @@ import { setMetaData } from '../../validators/validators'; import { checkCreateTriggerRoute, failCTWithoutData, - passCT, + passCT2, failCTDuplicateTrigger, insertTableRow, deleteCTTestTrigger, deleteCTTestTable, passPTCreateTable, + passCT1, createEtTransform, modifyEtTransform, deleteEtTransform, @@ -37,7 +38,8 @@ export const runCreateTriggerTests = () => { checkCreateTriggerRoute ); it('Fails to create trigger without data', failCTWithoutData); - it('Successfuly creates trigger', passCT); + it('Successfuly creates trigger with selected columns for update', passCT1); + it('Successfuly creates trigger with all columns for update', passCT2); it('Fails to create duplicate trigger', failCTDuplicateTrigger); it('Insert a row and invoke trigger', insertTableRow); it("Delete's the test trigger", deleteCTTestTrigger); diff --git a/console/cypress/integration/validators/validators.ts b/console/cypress/integration/validators/validators.ts index fa7813cebeb..43328ea86ea 100644 --- a/console/cypress/integration/validators/validators.ts +++ b/console/cypress/integration/validators/validators.ts @@ -475,7 +475,8 @@ export const validateCTrigger = ( triggerName: string, tableName: string, schemaName = 'public', - result: ResultType + result: ResultType, + allCols?: boolean ) => { const reqBody = { type: 'export_metadata', @@ -505,7 +506,11 @@ export const validateCTrigger = ( expect(response.status === 200).to.be.true; expect(trigger.definition.insert.columns === '*').to.be.true; expect(trigger.definition.delete.columns === '*').to.be.true; - expect(trigger.definition.update.columns.length === 3).to.be.true; + if (allCols) { + expect(trigger.definition.update.columns === '*').to.be.true; + } else { + expect(trigger.definition.update.columns.length === 2).to.be.true; + } expect( trigger.retry_conf.interval_sec === parseInt(getIntervalSeconds(), 10) ).to.be.true; diff --git a/console/src/components/Services/Events/EventTriggers/Add/Add.tsx b/console/src/components/Services/Events/EventTriggers/Add/Add.tsx index 4ca9d672b34..8a664db62ba 100644 --- a/console/src/components/Services/Events/EventTriggers/Add/Add.tsx +++ b/console/src/components/Services/Events/EventTriggers/Add/Add.tsx @@ -361,6 +361,7 @@ const Add: React.FC = props => { state={state} databaseInfo={databaseInfo} dataSourcesList={dataSourcesList} + readOnlyMode={readOnlyMode} handleTriggerNameChange={handleTriggerNameChange} handleWebhookValueChange={handleWebhookValueChange} handleWebhookTypeChange={handleWebhookTypeChange} @@ -371,6 +372,7 @@ const Add: React.FC = props => { handleOperationsColumnsChange={handleOperationsColumnsChange} handleRetryConfChange={handleRetryConfChange} handleHeadersChange={handleHeadersChange} + handleToggleAllColumn={setState.toggleAllColumnChecked} /> ) => void; handleWebhookValueChange: (v: string) => void; handleWebhookTypeChange: (e: React.BaseSyntheticEvent) => void; @@ -32,6 +33,7 @@ type CreateETFormProps = { handleOperationsColumnsChange: (oc: ETOperationColumn[]) => void; handleRetryConfChange: (r: RetryConf) => void; handleHeadersChange: (h: Header[]) => void; + handleToggleAllColumn: () => void; }; const CreateETForm: React.FC = props => { @@ -45,9 +47,11 @@ const CreateETForm: React.FC = props => { retryConf, operations, operationColumns, + isAllColumnChecked, }, databaseInfo, dataSourcesList, + readOnlyMode, handleTriggerNameChange, handleDatabaseChange, handleSchemaChange, @@ -58,6 +62,7 @@ const CreateETForm: React.FC = props => { handleOperationsColumnsChange, handleRetryConfChange, handleHeadersChange, + handleToggleAllColumn, } = props; const supportedDrivers = getSupportedDrivers('events.triggers.add'); @@ -195,6 +200,9 @@ const CreateETForm: React.FC = props => { diff --git a/console/src/components/Services/Events/EventTriggers/Common/ColumnList.tsx b/console/src/components/Services/Events/EventTriggers/Common/ColumnList.tsx index 3fce20f870f..42dc63392ba 100644 --- a/console/src/components/Services/Events/EventTriggers/Common/ColumnList.tsx +++ b/console/src/components/Services/Events/EventTriggers/Common/ColumnList.tsx @@ -2,15 +2,26 @@ import React from 'react'; import { QualifiedTable } from '@/metadata/types'; import styles from '../TableCommon/EventTable.scss'; import { ETOperationColumn } from '../../types'; +import { ColumnSelectionRadioButton } from './ColumnSelectionRadioButton'; type ColumnListProps = { operationColumns: ETOperationColumn[]; table: QualifiedTable; + isAllColumnChecked: boolean; + readOnlyMode: boolean; + handleToggleAllColumn: () => void; handleOperationsColumnsChange: (oc: ETOperationColumn[]) => void; }; const ColumnList: React.FC = props => { - const { operationColumns, table, handleOperationsColumnsChange } = props; + const { + operationColumns, + table, + isAllColumnChecked, + readOnlyMode, + handleToggleAllColumn, + handleOperationsColumnsChange, + } = props; if (!table.name) { return Select a table first to get column list; @@ -28,24 +39,37 @@ const ColumnList: React.FC = props => { return ( <> - {operationColumns.map(opCol => ( -
-
- -
-
- ))} + +
+ {!isAllColumnChecked ? ( + <> +
List of columns to select:
+ {operationColumns.map(opCol => ( +
+
+ +
+
+ ))} + + ) : null} +
); }; diff --git a/console/src/components/Services/Events/EventTriggers/Common/ColumnSelectionRadioButton.tsx b/console/src/components/Services/Events/EventTriggers/Common/ColumnSelectionRadioButton.tsx new file mode 100644 index 00000000000..f132f5fac28 --- /dev/null +++ b/console/src/components/Services/Events/EventTriggers/Common/ColumnSelectionRadioButton.tsx @@ -0,0 +1,41 @@ +import React from 'react'; + +interface InputProps extends React.ComponentProps<'input'> { + isAllColumnChecked: boolean; + handleColumnRadioButton: () => void; + readOnly: boolean; +} + +export const ColumnSelectionRadioButton: React.FC = ({ + isAllColumnChecked, + handleColumnRadioButton, + readOnly, +}) => { + return ( +
+ + +
+ ); +}; diff --git a/console/src/components/Services/Events/EventTriggers/Modify/Modify.tsx b/console/src/components/Services/Events/EventTriggers/Modify/Modify.tsx index 7c38b888f74..9a1f126a600 100644 --- a/console/src/components/Services/Events/EventTriggers/Modify/Modify.tsx +++ b/console/src/components/Services/Events/EventTriggers/Modify/Modify.tsx @@ -326,6 +326,8 @@ const Modify: React.FC = props => { setOperationColumns={setState.operationColumns} styles={styles} save={saveWrapper('ops')} + isAllColumnChecked={state.isAllColumnChecked} + handleColumnRadioButton={setState.toggleAllColumnChecked} /> void; styles: Record; save: (success: VoidCallback, error: VoidCallback) => void; + isAllColumnChecked: boolean; + handleColumnRadioButton: () => void; }; const OperationEditor = (props: OperationEditorProps) => { @@ -35,8 +38,9 @@ const OperationEditor = (props: OperationEditorProps) => { operationColumns, setOperations, setOperationColumns, + isAllColumnChecked, + handleColumnRadioButton, } = props; - const etDef = currentTrigger.configuration.definition; const existingOps = parseEventTriggerOperations(etDef); const existingOpColumns = getETOperationColumns( @@ -73,7 +77,7 @@ const OperationEditor = (props: OperationEditorProps) => {

Trigger columns to list to for updates. @@ -81,8 +85,25 @@ const OperationEditor = (props: OperationEditorProps) => {

- {ops.update ? ( - opCols.map(col => { +
+ {ops.update ? ( + + ) : ( +
+ Applicable only if update operation is selected. +
+ )} +
+
+ {!isAllColumnChecked ? ( +
+ {opCols.map(col => { const toggle = () => { if (!readOnly) { const newCols = opCols.map(oc => { @@ -111,15 +132,9 @@ const OperationEditor = (props: OperationEditorProps) => { ({col.type}) ); - }) - ) : ( -
- Applicable only if update operation is selected. -
- )} -
+ })} +
+ ) : null} ); diff --git a/console/src/components/Services/Events/EventTriggers/state.ts b/console/src/components/Services/Events/EventTriggers/state.ts index 8b9006a672f..2390d54a878 100644 --- a/console/src/components/Services/Events/EventTriggers/state.ts +++ b/console/src/components/Services/Events/EventTriggers/state.ts @@ -27,6 +27,7 @@ export type LocalEventTriggerState = { retryConf: RetryConf; headers: Header[]; source: string; + isAllColumnChecked: boolean; }; const defaultState: LocalEventTriggerState = { @@ -53,6 +54,7 @@ const defaultState: LocalEventTriggerState = { }, headers: [defaultHeader], source: '', + isAllColumnChecked: true, }; export const parseServerETDefinition = ( @@ -86,6 +88,7 @@ export const parseServerETDefinition = ( webhook: parseServerWebhook(etConf.webhook, etConf.webhook_from_env), retryConf: etConf.retry_conf, headers: parseServerHeaders(eventTrigger.configuration.headers), + isAllColumnChecked: etDef.update.columns === '*', }; }; @@ -161,6 +164,12 @@ export const useEventTrigger = (initState?: LocalEventTriggerState) => { bulk: (s: LocalEventTriggerState) => { setState(s); }, + toggleAllColumnChecked: () => { + setState(s => ({ + ...s, + isAllColumnChecked: !s.isAllColumnChecked, + })); + }, }, }; }; diff --git a/console/src/components/Services/Events/ServerIO.ts b/console/src/components/Services/Events/ServerIO.ts index 60861bf0484..dbd092881bb 100644 --- a/console/src/components/Services/Events/ServerIO.ts +++ b/console/src/components/Services/Events/ServerIO.ts @@ -422,9 +422,11 @@ export const modifyEventTrigger = ( insert: state.operations.insert ? { columns: '*' } : null, update: state.operations.update ? { - columns: state.operationColumns - .filter(c => !!c.enabled) - .map(c => c.name), + columns: state.isAllColumnChecked + ? '*' + : state.operationColumns + .filter(c => !!c.enabled) + .map(c => c.name), } : null, delete: state.operations.delete ? { columns: '*' } : null, diff --git a/console/src/metadata/queryUtils.ts b/console/src/metadata/queryUtils.ts index 64a366eb1db..576f1e52948 100644 --- a/console/src/metadata/queryUtils.ts +++ b/console/src/metadata/queryUtils.ts @@ -450,9 +450,9 @@ export const generateCreateEventTriggerQuery = ( : null, update: state.operations.update ? { - columns: state.operationColumns - .filter(c => !!c.enabled) - .map(c => c.name), + columns: state.isAllColumnChecked + ? '*' + : state.operationColumns.filter(c => !!c.enabled).map(c => c.name), } : null, delete: state.operations.delete