Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import app from "../../aitable_ai.app.mjs";

export default {
key: "aitable_ai-create-datasheet",
name: "Create Datasheet",
description: "Create a datasheet in the specified space. [See the documentation](https://developers.aitable.ai/api/reference#tag/Datasheet/operation/create-datasheets)",
version: "0.0.1",
type: "action",
props: {
app,
spaceId: {
propDefinition: [
app,
"spaceId",
],
},
name: {
propDefinition: [
app,
"name",
],
},
description: {
propDefinition: [
app,
"description",
],
},
folderId: {
propDefinition: [
app,
"folderId",
],
},
},

async run({ $ }) {
const response = await this.app.createDatasheet({
$,
spaceId: this.spaceId,
data: {
name: this.name,
description: this.description,
folderId: this.folderId,
},
});
$.export("$summary", `Successfully created Datasheet with ID '${response.data.id}'`);
return response;
},
Comment on lines +37 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add response validation and explicit error handling.

While the implementation is clean, consider these improvements:

  1. Validate the response structure before accessing response.data.id
  2. Add explicit error handling for common failure scenarios

Consider this enhanced implementation:

 async run({ $ }) {
+  try {
     const response = await this.app.createDatasheet({
       $,
       spaceId: this.spaceId,
       data: {
         name: this.name,
         description: this.description,
         folderId: this.folderId,
       },
     });
+    if (!response?.data?.id) {
+      throw new Error('Invalid response: Missing datasheet ID');
+    }
     $.export("$summary", `Successfully created Datasheet with ID '${response.data.id}'`);
     return response;
+  } catch (error) {
+    throw new Error(`Failed to create datasheet: ${error.message}`);
+  }
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async run({ $ }) {
const response = await this.app.createDatasheet({
$,
spaceId: this.spaceId,
data: {
name: this.name,
description: this.description,
folderId: this.folderId,
},
});
$.export("$summary", `Successfully created Datasheet with ID '${response.data.id}'`);
return response;
},
async run({ $ }) {
try {
const response = await this.app.createDatasheet({
$,
spaceId: this.spaceId,
data: {
name: this.name,
description: this.description,
folderId: this.folderId,
},
});
if (!response?.data?.id) {
throw new Error('Invalid response: Missing datasheet ID');
}
$.export("$summary", `Successfully created Datasheet with ID '${response.data.id}'`);
return response;
} catch (error) {
throw new Error(`Failed to create datasheet: ${error.message}`);
}
},

};
44 changes: 44 additions & 0 deletions components/aitable_ai/actions/create-field/create-field.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import app from "../../aitable_ai.app.mjs";

export default {
key: "aitable_ai-create-field",
name: "Create Field",
description: "Create a new field in the specified datasheet. [See the documentation](https://developers.aitable.ai/api/reference#tag/Field/operation/create-fields)",
version: "0.0.1",
type: "action",
props: {
app,
spaceId: {
propDefinition: [
app,
"spaceId",
],
},
type: {
propDefinition: [
app,
"type",
],
},
name: {
propDefinition: [
app,
"name",
],
description: "Name of the Field",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
description: "Name of the Field",
description: "Name of the field",

},
},

async run({ $ }) {
const response = await this.app.createField({
$,
spaceId: this.spaceId,
data: {
type: this.type,
name: this.name,
},
});
$.export("$summary", `Successfully sent request to create field. Result: '${response.message}'`);
return response;
},
Comment on lines +32 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling and input validation.

The current implementation could be more robust with:

  1. Try-catch block for API error handling
  2. Input validation before the API call
  3. Safe access to response message

Apply this diff to improve error handling and validation:

   async run({ $ }) {
+    // Validate inputs
+    if (!this.name.trim()) {
+      throw new Error("Field name cannot be empty");
+    }
+
+    try {
       const response = await this.app.createField({
         $,
         spaceId: this.spaceId,
         data: {
           type: this.type,
           name: this.name,
         },
       });
-      $.export("$summary", `Successfully sent request to create field. Result: '${response.message}'`);
+      $.export("$summary", `Successfully created field "${this.name}" of type "${this.type}". ${response?.message || ''}`);
       return response;
+    } catch (error) {
+      throw new Error(`Failed to create field: ${error.message}`);
+    }
   },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async run({ $ }) {
const response = await this.app.createField({
$,
spaceId: this.spaceId,
data: {
type: this.type,
name: this.name,
},
});
$.export("$summary", `Successfully sent request to create field. Result: '${response.message}'`);
return response;
},
async run({ $ }) {
// Validate inputs
if (!this.name.trim()) {
throw new Error("Field name cannot be empty");
}
try {
const response = await this.app.createField({
$,
spaceId: this.spaceId,
data: {
type: this.type,
name: this.name,
},
});
$.export("$summary", `Successfully created field "${this.name}" of type "${this.type}". ${response?.message || ''}`);
return response;
} catch (error) {
throw new Error(`Failed to create field: ${error.message}`);
}
},

};
34 changes: 34 additions & 0 deletions components/aitable_ai/actions/delete-field/delete-field.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import app from "../../aitable_ai.app.mjs";

export default {
key: "aitable_ai-delete-field",
name: "Delete Field",
description: "Delete a field in the specified datasheet. [See the documentation](https://developers.aitable.ai/api/reference/#tag/Field/operation/delete-fields)",
version: "0.0.1",
type: "action",
props: {
app,
spaceId: {
propDefinition: [
app,
"spaceId",
],
},
fieldId: {
propDefinition: [
app,
"fieldId",
],
},
},
Comment on lines +9 to +23
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding input validation for spaceId and fieldId.

While the props are correctly defined, adding validation rules (e.g., format checks, required flags) could improve error handling and user experience.

Consider enhancing the props with validation:

 spaceId: {
   propDefinition: [
     app,
     "spaceId",
   ],
+  description: "The ID of the space containing the field to delete",
+  optional: false,
 },
 fieldId: {
   propDefinition: [
     app,
     "fieldId",
   ],
+  description: "The ID of the field to delete",
+  optional: false,
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
props: {
app,
spaceId: {
propDefinition: [
app,
"spaceId",
],
},
fieldId: {
propDefinition: [
app,
"fieldId",
],
},
},
props: {
app,
spaceId: {
propDefinition: [
app,
"spaceId",
],
description: "The ID of the space containing the field to delete",
optional: false,
},
fieldId: {
propDefinition: [
app,
"fieldId",
],
description: "The ID of the field to delete",
optional: false,
},
},


async run({ $ }) {
const response = await this.app.deleteField({
$,
spaceId: this.spaceId,
fieldId: this.fieldId,
});
$.export("$summary", `Successfully deleted the field with ID '${this.fieldId}'`);
return response;
},
Comment on lines +25 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling and user confirmation for field deletion.

The current implementation could benefit from:

  1. Try-catch block for API error handling
  2. Response validation
  3. Warning about the irreversible nature of deletion

Consider implementing these improvements:

 async run({ $ }) {
+  const confirmMessage = "Are you sure you want to delete this field? This action cannot be undone.";
+  if (!await $.confirm(confirmMessage)) {
+    throw new Error("User cancelled the operation");
+  }
+
+  try {
     const response = await this.app.deleteField({
       $,
       spaceId: this.spaceId,
       fieldId: this.fieldId,
     });
+
+    if (!response || response.error) {
+      throw new Error(response?.error?.message || "Failed to delete field");
+    }
+
     $.export("$summary", `Successfully deleted the field with ID '${this.fieldId}'`);
     return response;
+  } catch (error) {
+    throw new Error(`Failed to delete field: ${error.message}`);
+  }
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async run({ $ }) {
const response = await this.app.deleteField({
$,
spaceId: this.spaceId,
fieldId: this.fieldId,
});
$.export("$summary", `Successfully deleted the field with ID '${this.fieldId}'`);
return response;
},
async run({ $ }) {
const confirmMessage = "Are you sure you want to delete this field? This action cannot be undone.";
if (!await $.confirm(confirmMessage)) {
throw new Error("User cancelled the operation");
}
try {
const response = await this.app.deleteField({
$,
spaceId: this.spaceId,
fieldId: this.fieldId,
});
if (!response || response.error) {
throw new Error(response?.error?.message || "Failed to delete field");
}
$.export("$summary", `Successfully deleted the field with ID '${this.fieldId}'`);
return response;
} catch (error) {
throw new Error(`Failed to delete field: ${error.message}`);
}
},

};
120 changes: 115 additions & 5 deletions components/aitable_ai/aitable_ai.app.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,121 @@
import { axios } from "@pipedream/platform";
import constants from "./common/constants.mjs";

export default {
type: "app",
app: "aitable_ai",
propDefinitions: {},
propDefinitions: {
spaceId: {
type: "string",
label: "Space ID",
description: "ID of the Space",
async options() {
const response = await this.getSpaces({});
const spaceIds = response.data.spaces;
return spaceIds.map(({
id, name,
}) => ({
value: id,
label: name,
}));
},
},
fieldId: {
type: "string",
label: "Field ID",
description: "ID of the Field",
async options() {
const response = await this.getFields({});
const fieldIds = response.data.fields;
return fieldIds.map(({
id, name,
}) => ({
value: id,
label: name,
}));
},
},
Comment on lines +23 to +37
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure datasheetId is provided for fetching fields in fieldId options

The fieldId propDefinition's options method relies on this.$auth.datasheet_id, which may not be present in the authentication object. To ensure the correct datasheet is used, consider adding datasheetId as a prop and updating the options method accordingly.

Apply this diff to include datasheetId and adjust the options method:

+datasheetId: {
+  type: "string",
+  label: "Datasheet ID",
+  description: "ID of the Datasheet",
+},
 fieldId: {
   type: "string",
   label: "Field ID",
   description: "ID of the Field",
   async options() {
+    const datasheetId = this.datasheetId;
     const response = await this.getFields(datasheetId);
     const fieldIds = response.data.fields;
     return fieldIds.map(({
       id, name,
     }) => ({
       value: id,
       label: name,
     }));
   },
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fieldId: {
type: "string",
label: "Field ID",
description: "ID of the Field",
async options() {
const response = await this.getFields({});
const fieldIds = response.data.fields;
return fieldIds.map(({
id, name,
}) => ({
value: id,
label: name,
}));
},
},
datasheetId: {
type: "string",
label: "Datasheet ID",
description: "ID of the Datasheet",
},
fieldId: {
type: "string",
label: "Field ID",
description: "ID of the Field",
async options() {
const datasheetId = this.datasheetId;
const response = await this.getFields(datasheetId);
const fieldIds = response.data.fields;
return fieldIds.map(({
id, name,
}) => ({
value: id,
label: name,
}));
},
},

name: {
type: "string",
label: "Name",
description: "Name of the Datasheet",
},
description: {
type: "string",
label: "Description",
description: "Description of the Datasheet",
},
folderId: {
type: "string",
label: "Folder ID",
description: "The Folder ID is located in the `URL` when the folder is selected on the `Workbench page`, i.e.: if the URL is `https://aitable.ai/workbench/123456`, the `Folder ID` is 123456",
optional: true,
},
type: {
type: "string",
label: "Type",
description: "Type of the Field",
options: constants.FIELD_TYPES,
},
},
methods: {
// this.$auth contains connected account data
authKeys() {
console.log(Object.keys(this.$auth));
_baseUrl() {
return "https://aitable.ai/fusion/v1";
},
async _makeRequest(opts = {}) {
const {
$ = this,
path,
headers,
...otherOpts
} = opts;
return axios($, {
...otherOpts,
url: this._baseUrl() + path,
headers: {
...headers,
Authorization: `Bearer ${this.$auth.api_token}`,
},
});
},
async createDatasheet({
spaceId, ...args
}) {
return this._makeRequest({
path: `/spaces/${spaceId}/datasheets`,
method: "post",
...args,
});
},
async createField({
spaceId, ...args
}) {
return this._makeRequest({
path: `/spaces/${spaceId}/datasheets/${this.$auth.datasheet_id}/fields`,
method: "post",
...args,
});
},
Comment on lines +90 to +98
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure datasheetId is provided in createField method

In the createField method, this.$auth.datasheet_id is used, which may not exist in the authentication object. Accepting datasheetId as a parameter ensures the correct datasheet is targeted.

Apply this diff to adjust the method:

 async createField({
   spaceId,
+  datasheetId,
   ...args
 }) {
   return this._makeRequest({
     path: `/spaces/${spaceId}/datasheets/${datasheetId}/fields`,
     method: "post",
     ...args,
   });
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async createField({
spaceId, ...args
}) {
return this._makeRequest({
path: `/spaces/${spaceId}/datasheets/${this.$auth.datasheet_id}/fields`,
method: "post",
...args,
});
},
async createField({
spaceId,
datasheetId,
...args
}) {
return this._makeRequest({
path: `/spaces/${spaceId}/datasheets/${datasheetId}/fields`,
method: "post",
...args,
});
},

async deleteField({
spaceId, fieldId, ...args
}) {
return this._makeRequest({
path: `/spaces/${spaceId}/datasheets/${this.$auth.datasheet_id}/fields/${fieldId}`,
method: "delete",
...args,
});
},
Comment on lines +99 to +107
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure datasheetId is provided in deleteField method

Similarly, the deleteField method references this.$auth.datasheet_id. Accepting datasheetId as a parameter will make the method more robust.

Apply this diff:

 async deleteField({
   spaceId,
   fieldId,
+  datasheetId,
   ...args
 }) {
   return this._makeRequest({
     path: `/spaces/${spaceId}/datasheets/${datasheetId}/fields/${fieldId}`,
     method: "delete",
     ...args,
   });
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async deleteField({
spaceId, fieldId, ...args
}) {
return this._makeRequest({
path: `/spaces/${spaceId}/datasheets/${this.$auth.datasheet_id}/fields/${fieldId}`,
method: "delete",
...args,
});
},
async deleteField({
spaceId,
fieldId,
datasheetId,
...args
}) {
return this._makeRequest({
path: `/spaces/${spaceId}/datasheets/${datasheetId}/fields/${fieldId}`,
method: "delete",
...args,
});
},

async getSpaces(args = {}) {
return this._makeRequest({
path: "/spaces",
...args,
});
},
async getFields(args = {}) {
return this._makeRequest({
path: `/datasheets/${this.$auth.datasheet_id}/fields`,
...args,
});
Comment on lines +114 to +118
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ensure datasheetId is provided in getFields method

The getFields method currently uses this.$auth.datasheet_id. For flexibility and reliability, accept datasheetId as a parameter.

Apply this diff:

 async getFields(datasheetId, args = {}) {
   return this._makeRequest({
     path: `/datasheets/${datasheetId}/fields`,
     ...args,
   });
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async getFields(args = {}) {
return this._makeRequest({
path: `/datasheets/${this.$auth.datasheet_id}/fields`,
...args,
});
async getFields(datasheetId, args = {}) {
return this._makeRequest({
path: `/datasheets/${datasheetId}/fields`,
...args,
});

},
},
};
};
11 changes: 11 additions & 0 deletions components/aitable_ai/common/constants.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default {
FIELD_TYPES: [
"Text",
"URL",
"Phone",
"Email",
"WorkDoc",
"AutoNumber",
"CreatedBy",
],
};
7 changes: 5 additions & 2 deletions components/aitable_ai/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pipedream/aitable_ai",
"version": "0.0.1",
"version": "0.1.0",
"description": "Pipedream AITable.ai Components",
"main": "aitable_ai.app.mjs",
"keywords": [
Expand All @@ -11,5 +11,8 @@
"author": "Pipedream <[email protected]> (https://pipedream.com/)",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@pipedream/platform": "^3.0.3"
}
}
}
5 changes: 4 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading