diff --git a/packages/backend/src/apps/airtable/actions/create-record/index.js b/packages/backend/src/apps/airtable/actions/create-record/index.js
new file mode 100644
index 00000000..554015be
--- /dev/null
+++ b/packages/backend/src/apps/airtable/actions/create-record/index.js
@@ -0,0 +1,92 @@
+import defineAction from '../../../../helpers/define-action.js';
+
+export default defineAction({
+ name: 'Create record',
+ key: 'createRecord',
+ description: 'Creates a new record with fields that automatically populate.',
+ arguments: [
+ {
+ label: 'Base',
+ key: 'baseId',
+ type: 'dropdown',
+ required: true,
+ description: 'Base in which to create the record.',
+ variables: true,
+ source: {
+ type: 'query',
+ name: 'getDynamicData',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listBases',
+ },
+ ],
+ },
+ },
+ {
+ label: 'Table',
+ key: 'tableId',
+ type: 'dropdown',
+ required: true,
+ dependsOn: ['parameters.baseId'],
+ description: '',
+ variables: true,
+ source: {
+ type: 'query',
+ name: 'getDynamicData',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listTables',
+ },
+ {
+ name: 'parameters.baseId',
+ value: '{parameters.baseId}',
+ },
+ ],
+ },
+ additionalFields: {
+ type: 'query',
+ name: 'getDynamicFields',
+ arguments: [
+ {
+ name: 'key',
+ value: 'listFields',
+ },
+ {
+ name: 'parameters.baseId',
+ value: '{parameters.baseId}',
+ },
+ {
+ name: 'parameters.tableId',
+ value: '{parameters.tableId}',
+ },
+ ],
+ },
+ },
+ ],
+
+ async run($) {
+ const { baseId, tableId, ...rest } = $.step.parameters;
+
+ const fields = Object.entries(rest).reduce((result, [key, value]) => {
+ if (Array.isArray(value)) {
+ result[key] = value.map((item) => item.value);
+ } else if (value !== '') {
+ result[key] = value;
+ }
+ return result;
+ }, {});
+
+ const body = {
+ typecast: true,
+ fields,
+ };
+
+ const { data } = await $.http.post(`/v0/${baseId}/${tableId}`, body);
+
+ $.setActionItem({
+ raw: data,
+ });
+ },
+});
diff --git a/packages/backend/src/apps/airtable/actions/index.js b/packages/backend/src/apps/airtable/actions/index.js
new file mode 100644
index 00000000..abe026dd
--- /dev/null
+++ b/packages/backend/src/apps/airtable/actions/index.js
@@ -0,0 +1,3 @@
+import createRecord from './create-record/index.js';
+
+export default [createRecord];
diff --git a/packages/backend/src/apps/airtable/dynamic-data/index.js b/packages/backend/src/apps/airtable/dynamic-data/index.js
new file mode 100644
index 00000000..344ff5d1
--- /dev/null
+++ b/packages/backend/src/apps/airtable/dynamic-data/index.js
@@ -0,0 +1,4 @@
+import listBases from './list-bases/index.js';
+import listTables from './list-tables/index.js';
+
+export default [listBases, listTables];
diff --git a/packages/backend/src/apps/airtable/dynamic-data/list-bases/index.js b/packages/backend/src/apps/airtable/dynamic-data/list-bases/index.js
new file mode 100644
index 00000000..2f075694
--- /dev/null
+++ b/packages/backend/src/apps/airtable/dynamic-data/list-bases/index.js
@@ -0,0 +1,28 @@
+export default {
+ name: 'List bases',
+ key: 'listBases',
+
+ async run($) {
+ const bases = {
+ data: [],
+ };
+
+ const params = {};
+
+ do {
+ const { data } = await $.http.get('/v0/meta/bases', { params });
+ params.offset = data.offset;
+
+ if (data?.bases) {
+ for (const base of data.bases) {
+ bases.data.push({
+ value: base.id,
+ name: base.name,
+ });
+ }
+ }
+ } while (params.offset);
+
+ return bases;
+ },
+};
diff --git a/packages/backend/src/apps/airtable/dynamic-data/list-tables/index.js b/packages/backend/src/apps/airtable/dynamic-data/list-tables/index.js
new file mode 100644
index 00000000..90d6b4c0
--- /dev/null
+++ b/packages/backend/src/apps/airtable/dynamic-data/list-tables/index.js
@@ -0,0 +1,35 @@
+export default {
+ name: 'List tables',
+ key: 'listTables',
+
+ async run($) {
+ const tables = {
+ data: [],
+ };
+ const baseId = $.step.parameters.baseId;
+
+ if (!baseId) {
+ return tables;
+ }
+
+ const params = {};
+
+ do {
+ const { data } = await $.http.get(`/v0/meta/bases/${baseId}/tables`, {
+ params,
+ });
+ params.offset = data.offset;
+
+ if (data?.tables) {
+ for (const table of data.tables) {
+ tables.data.push({
+ value: table.id,
+ name: table.name,
+ });
+ }
+ }
+ } while (params.offset);
+
+ return tables;
+ },
+};
diff --git a/packages/backend/src/apps/airtable/dynamic-fields/index.js b/packages/backend/src/apps/airtable/dynamic-fields/index.js
new file mode 100644
index 00000000..5d97313e
--- /dev/null
+++ b/packages/backend/src/apps/airtable/dynamic-fields/index.js
@@ -0,0 +1,3 @@
+import listFields from './list-fields/index.js';
+
+export default [listFields];
diff --git a/packages/backend/src/apps/airtable/dynamic-fields/list-fields/index.js b/packages/backend/src/apps/airtable/dynamic-fields/list-fields/index.js
new file mode 100644
index 00000000..704d1940
--- /dev/null
+++ b/packages/backend/src/apps/airtable/dynamic-fields/list-fields/index.js
@@ -0,0 +1,86 @@
+const hasValue = (value) => value !== null && value !== undefined;
+
+export default {
+ name: 'List fields',
+ key: 'listFields',
+
+ async run($) {
+ const options = [];
+ const { baseId, tableId } = $.step.parameters;
+
+ if (!hasValue(baseId) || !hasValue(tableId)) {
+ return;
+ }
+
+ const { data } = await $.http.get(`/v0/meta/bases/${baseId}/tables`);
+
+ const selectedTable = data.tables.find((table) => table.id === tableId);
+
+ if (!selectedTable) return;
+
+ selectedTable.fields.forEach((field) => {
+ if (field.type === 'singleSelect') {
+ options.push({
+ label: field.name,
+ key: field.name,
+ type: 'dropdown',
+ required: false,
+ variables: true,
+ options: field.options.choices.map((choice) => ({
+ label: choice.name,
+ value: choice.id,
+ })),
+ });
+ } else if (field.type === 'multipleSelects') {
+ options.push({
+ label: field.name,
+ key: field.name,
+ type: 'dynamic',
+ required: false,
+ variables: true,
+ fields: [
+ {
+ label: 'Value',
+ key: 'value',
+ type: 'dropdown',
+ required: false,
+ variables: true,
+ options: field.options.choices.map((choice) => ({
+ label: choice.name,
+ value: choice.id,
+ })),
+ },
+ ],
+ });
+ } else if (field.type === 'checkbox') {
+ options.push({
+ label: field.name,
+ key: field.name,
+ type: 'dropdown',
+ required: false,
+ variables: true,
+ options: [
+ {
+ label: 'Yes',
+ value: 'true',
+ },
+ {
+ label: 'No',
+ value: 'false',
+ },
+ ],
+ });
+ } else {
+ options.push({
+ label: field.name,
+ key: field.name,
+ type: 'string',
+ required: false,
+ variables: true,
+ });
+ }
+ });
+
+ return options;
+ },
+};
diff --git a/packages/backend/src/apps/airtable/index.js b/packages/backend/src/apps/airtable/index.js
index c658c6ab..b2bb56a0 100644
--- a/packages/backend/src/apps/airtable/index.js
+++ b/packages/backend/src/apps/airtable/index.js
@@ -1,6 +1,9 @@
import defineApp from '../../helpers/define-app.js';
import addAuthHeader from './common/add-auth-header.js';
import auth from './auth/index.js';
+import actions from './actions/index.js';
+import dynamicData from './dynamic-data/index.js';
+import dynamicFields from './dynamic-fields/index.js';
export default defineApp({
name: 'Airtable',
@@ -13,4 +16,7 @@ export default defineApp({
supportsConnections: true,
beforeRequest: [addAuthHeader],
auth,
+ actions,
+ dynamicData,
+ dynamicFields,
});
diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js
index d5fee6f8..86d71774 100644
--- a/packages/docs/pages/.vitepress/config.js
+++ b/packages/docs/pages/.vitepress/config.js
@@ -36,7 +36,10 @@ export default defineConfig({
text: 'Airtable',
collapsible: true,
collapsed: true,
- items: [{ text: 'Connection', link: '/apps/airtable/connection' }],
+ items: [
+ { text: 'Actions', link: '/apps/airtable/actions' },
+ { text: 'Connection', link: '/apps/airtable/connection' },
+ ],
},
{
text: 'Carbone',
diff --git a/packages/docs/pages/apps/airtable/actions.md b/packages/docs/pages/apps/airtable/actions.md
new file mode 100644
index 00000000..e496a373
--- /dev/null
+++ b/packages/docs/pages/apps/airtable/actions.md
@@ -0,0 +1,12 @@
+---
+favicon: /favicons/airtable.svg
+items:
+ - name: Create record
+ desc: Creates a new record with fields that automatically populate.
+---
+
+
+
+
diff --git a/packages/docs/pages/guide/available-apps.md b/packages/docs/pages/guide/available-apps.md
index 90f8ba0a..49242ef3 100644
--- a/packages/docs/pages/guide/available-apps.md
+++ b/packages/docs/pages/guide/available-apps.md
@@ -2,6 +2,7 @@
The following integrations are currently supported by Automatisch.
+- [Airtable](/apps/airtable/actions)
- [Carbone](/apps/carbone/actions)
- [DeepL](/apps/deepl/actions)
- [Delay](/apps/delay/actions)