diff --git a/packages/backend/src/apps/google-sheets/actions/create-spreadsheet-row/index.ts b/packages/backend/src/apps/google-sheets/actions/create-spreadsheet-row/index.ts new file mode 100644 index 00000000..e9e6ac8b --- /dev/null +++ b/packages/backend/src/apps/google-sheets/actions/create-spreadsheet-row/index.ts @@ -0,0 +1,145 @@ +import defineAction from '../../../../helpers/define-action'; + +type TSheetsResponse = { + sheets: { + properties: { + sheetId: string; + title: string; + }; + }[]; +}; + +export default defineAction({ + name: 'Create Spreadsheet Row', + key: 'createSpreadsheetRow', + description: 'Creates a new row in a specific spreadsheet.', + arguments: [ + { + label: 'Drive', + key: 'driveId', + type: 'dropdown' as const, + required: false, + description: + 'The Google Drive where your spreadsheet resides. If nothing is selected, then your personal Google Drive will be used.', + variables: false, + source: { + type: 'query', + name: 'getDynamicData', + arguments: [ + { + name: 'key', + value: 'listDrives', + }, + ], + }, + }, + { + label: 'Spreadsheet', + key: 'spreadsheetId', + type: 'dropdown' as const, + required: true, + dependsOn: ['parameters.driveId'], + description: 'The spreadsheets in your Google Drive.', + variables: false, + source: { + type: 'query', + name: 'getDynamicData', + arguments: [ + { + name: 'key', + value: 'listSpreadsheets', + }, + { + name: 'parameters.driveId', + value: '{parameters.driveId}', + }, + ], + }, + }, + { + label: 'Worksheet', + key: 'worksheetId', + type: 'dropdown' as const, + required: true, + dependsOn: ['parameters.spreadsheetId'], + description: 'The worksheets in your selected spreadsheet.', + variables: false, + source: { + type: 'query', + name: 'getDynamicData', + arguments: [ + { + name: 'key', + value: 'listWorksheets', + }, + { + name: 'parameters.spreadsheetId', + value: '{parameters.spreadsheetId}', + }, + ], + }, + additionalFields: { + type: 'query', + name: 'getDynamicFields', + arguments: [ + { + name: 'key', + value: 'listSheetHeaders', + }, + { + name: 'parameters.worksheetId', + value: '{parameters.worksheetId}', + }, + { + name: 'parameters.spreadsheetId', + value: '{parameters.spreadsheetId}', + }, + ], + }, + }, + ], + + async run($) { + const { + data: { sheets }, + } = await $.http.get( + `/v4/spreadsheets/${$.step.parameters.spreadsheetId}` + ); + + const selectedSheet = sheets.find( + (sheet) => sheet.properties.sheetId === $.step.parameters.worksheetId + ); + + const sheetName = selectedSheet.properties.title; + + const range = sheetName; + + const dataValues = Object.entries($.step.parameters) + .filter((entry: [string, string]) => entry[0].startsWith('header-')) + .map((value) => value[1]); + + const values = [dataValues]; + + const params = { + valueInputOption: 'USER_ENTERED', + insertDataOption: 'INSERT_ROWS', + includeValuesInResponse: true, + }; + + const body = { + majorDimension: 'ROWS', + range, + values, + }; + + const { data } = await $.http.post( + `/v4/spreadsheets/${$.step.parameters.spreadsheetId}/values/${range}:append`, + body, + { params } + ); + + $.setActionItem({ + raw: data, + }); + }, +}); diff --git a/packages/backend/src/apps/google-sheets/actions/index.ts b/packages/backend/src/apps/google-sheets/actions/index.ts new file mode 100644 index 00000000..7b0c8d2d --- /dev/null +++ b/packages/backend/src/apps/google-sheets/actions/index.ts @@ -0,0 +1,3 @@ +import createSpreadsheetRow from './create-spreadsheet-row'; + +export default [createSpreadsheetRow]; diff --git a/packages/backend/src/apps/google-sheets/dynamic-data/index.ts b/packages/backend/src/apps/google-sheets/dynamic-data/index.ts index 42da90b1..ac008c09 100644 --- a/packages/backend/src/apps/google-sheets/dynamic-data/index.ts +++ b/packages/backend/src/apps/google-sheets/dynamic-data/index.ts @@ -1,4 +1,5 @@ import listDrives from './list-drives'; import listSpreadsheets from './list-spreadsheets'; +import listWorksheets from './list-worksheets'; -export default [listDrives, listSpreadsheets]; +export default [listDrives, listSpreadsheets, listWorksheets]; diff --git a/packages/backend/src/apps/google-sheets/dynamic-data/list-worksheets/index.ts b/packages/backend/src/apps/google-sheets/dynamic-data/list-worksheets/index.ts new file mode 100644 index 00000000..b9aa5379 --- /dev/null +++ b/packages/backend/src/apps/google-sheets/dynamic-data/list-worksheets/index.ts @@ -0,0 +1,42 @@ +import { IGlobalVariable, IJSONObject } from '@automatisch/types'; + +export default { + name: 'List worksheets', + key: 'listWorksheets', + + async run($: IGlobalVariable) { + const spreadsheetId = $.step.parameters.spreadsheetId as string; + + const worksheets: { + data: IJSONObject[]; + } = { + data: [], + }; + + if (!spreadsheetId) { + return worksheets; + } + + const params: Record = { + pageToken: undefined as unknown as string, + }; + + do { + const { data } = await $.http.get(`/v4/spreadsheets/${spreadsheetId}`, { + params, + }); + params.pageToken = data.nextPageToken; + + if (data.sheets?.length) { + for (const sheet of data.sheets) { + worksheets.data.push({ + value: sheet.properties.sheetId, + name: sheet.properties.title, + }); + } + } + } while (params.pageToken); + + return worksheets; + }, +}; diff --git a/packages/backend/src/apps/google-sheets/dynamic-fields/index.ts b/packages/backend/src/apps/google-sheets/dynamic-fields/index.ts new file mode 100644 index 00000000..7f486818 --- /dev/null +++ b/packages/backend/src/apps/google-sheets/dynamic-fields/index.ts @@ -0,0 +1,3 @@ +import listSheetHeaders from './list-sheet-headers'; + +export default [listSheetHeaders]; diff --git a/packages/backend/src/apps/google-sheets/dynamic-fields/list-sheet-headers/index.ts b/packages/backend/src/apps/google-sheets/dynamic-fields/list-sheet-headers/index.ts new file mode 100644 index 00000000..4f4b5622 --- /dev/null +++ b/packages/backend/src/apps/google-sheets/dynamic-fields/list-sheet-headers/index.ts @@ -0,0 +1,67 @@ +import { IGlobalVariable } from '@automatisch/types'; + +type TSheetsResponse = { + sheets: { + properties: { + sheetId: string; + title: string; + }; + }[]; +}; + +type TSheetsValueResponse = { + majorDimension: string; + range: string; + values: string[][]; +}; + +export default { + name: 'List Sheet Headers', + key: 'listSheetHeaders', + + async run($: IGlobalVariable) { + if (!$.step.parameters.spreadsheetId || !$.step.parameters.worksheetId) { + return; + } + + const { + data: { sheets }, + } = await $.http.get( + `/v4/spreadsheets/${$.step.parameters.spreadsheetId}` + ); + + const selectedSheet = sheets.find( + (sheet) => sheet.properties.sheetId === $.step.parameters.worksheetId + ); + + const sheetName = selectedSheet.properties.title; + + const range = `${sheetName}!1:1`; + + const params: Record = { + majorDimension: 'ROWS', + }; + + const { data } = await $.http.get( + `/v4/spreadsheets/${$.step.parameters.spreadsheetId}/values/${range}`, + { + params, + } + ); + + if (!data.values) { + return; + } + + const result = data.values[0].map((item: string, index: number) => ({ + label: item, + key: `header-${index}`, + type: 'string' as const, + required: false, + value: item, + variables: true, + })); + + return result; + }, +}; diff --git a/packages/backend/src/apps/google-sheets/index.ts b/packages/backend/src/apps/google-sheets/index.ts index 5232b5f6..a8805ffd 100644 --- a/packages/backend/src/apps/google-sheets/index.ts +++ b/packages/backend/src/apps/google-sheets/index.ts @@ -2,7 +2,9 @@ import defineApp from '../../helpers/define-app'; import addAuthHeader from './common/add-auth-header'; import auth from './auth'; import triggers from './triggers'; +import actions from './actions'; import dynamicData from './dynamic-data'; +import dynamicFields from './dynamic-fields'; export default defineApp({ name: 'Google Sheets', @@ -16,5 +18,7 @@ export default defineApp({ beforeRequest: [addAuthHeader], auth, triggers, + actions, dynamicData, + dynamicFields, }); diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js index eb2d51a2..ad0a4dd1 100644 --- a/packages/docs/pages/.vitepress/config.js +++ b/packages/docs/pages/.vitepress/config.js @@ -120,6 +120,7 @@ export default defineConfig({ collapsed: true, items: [ { text: 'Triggers', link: '/apps/google-sheets/triggers' }, + { text: 'Actions', link: '/apps/google-sheets/actions' }, { text: 'Connection', link: '/apps/google-sheets/connection' }, ], }, diff --git a/packages/docs/pages/apps/google-sheets/actions.md b/packages/docs/pages/apps/google-sheets/actions.md new file mode 100644 index 00000000..87e5ebfe --- /dev/null +++ b/packages/docs/pages/apps/google-sheets/actions.md @@ -0,0 +1,12 @@ +--- +favicon: /favicons/google-sheets.svg +items: + - name: Create Spreadsheet Row + desc: Creates a new row in a specific spreadsheet +--- + + + + diff --git a/packages/web/src/hooks/useDynamicData.ts b/packages/web/src/hooks/useDynamicData.ts index 29284f4e..71c235de 100644 --- a/packages/web/src/hooks/useDynamicData.ts +++ b/packages/web/src/hooks/useDynamicData.ts @@ -4,7 +4,6 @@ import { useFormContext } from 'react-hook-form'; import set from 'lodash/set'; import type { UseFormReturn } from 'react-hook-form'; import isEqual from 'lodash/isEqual'; -import omit from 'lodash/omit'; import type { IField, IFieldDropdownSource, @@ -13,7 +12,7 @@ import type { import { GET_DYNAMIC_DATA } from 'graphql/queries/get-dynamic-data'; -const variableRegExp = /({.*?})/g; +const variableRegExp = /({.*?})/; function computeArguments( args: IFieldDropdownSource['arguments'], diff --git a/packages/web/src/hooks/useDynamicFields.ts b/packages/web/src/hooks/useDynamicFields.ts index c5a3f431..b6b8f855 100644 --- a/packages/web/src/hooks/useDynamicFields.ts +++ b/packages/web/src/hooks/useDynamicFields.ts @@ -12,7 +12,7 @@ import type { import { GET_DYNAMIC_FIELDS } from 'graphql/queries/get-dynamic-fields'; -const variableRegExp = /({.*?})/g; +const variableRegExp = /({.*?})/; // TODO: extract this function to a separate file function computeArguments( @@ -64,7 +64,10 @@ function useDynamicFields(stepId: string | undefined, schema: IField) { const computedVariables = React.useMemo(() => { if (schema.type === 'dropdown' && schema.additionalFields) { try { - const variables = computeArguments(schema.additionalFields.arguments, getValues); + const variables = computeArguments( + schema.additionalFields.arguments, + getValues + ); // if computed variables are the same, return the last computed variables. if (isEqual(variables, lastComputedVariables.current)) {