Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
79 changes: 79 additions & 0 deletions components/checkvist/actions/create-list-item/create-list-item.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import checkvist from "../../checkvist.app.mjs";
import { STATUS_OPTIONS } from "../../common/constants.mjs";
import { parseObject } from "../../common/utils.mjs";

export default {
key: "checkvist-create-list-item",
name: "Create List Item",
description: "Creates a new list item within a specified list. [See the documentation](https://checkvist.com/auth/api)",
version: "0.0.1",
type: "action",
props: {
checkvist,
listId: {
propDefinition: [
checkvist,
"listId",
],
},
content: {
type: "string",
label: "Content",
description: "Block of text containing items to add. Indentations indicate nested list items.",
},
parentId: {
propDefinition: [
checkvist,
"parentId",
({ listId }) => ({
listId,
}),
],
optional: true,
},
tags: {
type: "string[]",
label: "Tags",
description: "An array of tags.",
optional: true,
},
dueDate: {
type: "string",
label: "Due Date",
description: "Due for the task, in Checkvist's smart syntax format.",
optional: true,
},
position: {
type: "integer",
label: "Position",
description: "1-based position of the task (omit to add to the end of the list).",
optional: true,
},
status: {
type: "string",
label: "Status",
description: "Task status",
options: STATUS_OPTIONS,
optional: true,
},
},
async run({ $ }) {
const response = await this.checkvist.createListItem({
$,
listId: this.listId,
data: {
task: {
content: this.content,
parent_id: this.parentId || 0,
tags: parseObject(this.tags)?.join(","),
due_date: this.dueDate,
position: this.position,
status: this.status,
},
},
});

$.export("$summary", `Successfully created a new list item in list with ID ${this.listId}`);
return response;
},
Comment on lines +60 to +78
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 benefit from better error handling and input validation:

  1. No explicit error handling for API failures
  2. Silent failure possible in tags parsing
  3. No validation for required content field

Consider applying these improvements:

 async run({ $ }) {
+    if (!this.content?.trim()) {
+      throw new Error("Content cannot be empty");
+    }
+
+    const tags = this.tags ? parseObject(this.tags) : undefined;
+    if (this.tags && !tags) {
+      throw new Error("Invalid tags format");
+    }
+
     const response = await this.checkvist.createListItem({
       $,
       listId: this.listId,
       data: {
         task: {
           content: this.content,
           parent_id: this.parentId || 0,
-          tags: parseObject(this.tags)?.join(","),
+          tags: tags?.join(","),
           due_date: this.dueDate,
           position: this.position,
           status: this.status,
         },
       },
     });

     $.export("$summary", `Successfully created a new list item in list with ID ${this.listId}`);
     return response;
   },
📝 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.checkvist.createListItem({
$,
listId: this.listId,
data: {
task: {
content: this.content,
parent_id: this.parentId || 0,
tags: parseObject(this.tags)?.join(","),
due_date: this.dueDate,
position: this.position,
status: this.status,
},
},
});
$.export("$summary", `Successfully created a new list item in list with ID ${this.listId}`);
return response;
},
async run({ $ }) {
if (!this.content?.trim()) {
throw new Error("Content cannot be empty");
}
const tags = this.tags ? parseObject(this.tags) : undefined;
if (this.tags && !tags) {
throw new Error("Invalid tags format");
}
const response = await this.checkvist.createListItem({
$,
listId: this.listId,
data: {
task: {
content: this.content,
parent_id: this.parentId || 0,
tags: tags?.join(","),
due_date: this.dueDate,
position: this.position,
status: this.status,
},
},
});
$.export("$summary", `Successfully created a new list item in list with ID ${this.listId}`);
return response;
},

};
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import checkvist from "../../checkvist.app.mjs";
import {
SEPARATE_LINE_OPTIONS, STATUS_OPTIONS,
} from "../../common/constants.mjs";

export default {
key: "checkvist-create-multiple-list-items",
name: "Create Multiple List Items",
description: "Enables creation of several list items at once from a block of text. Indentations in the text indicate nested list items. [See the documentation](https://checkvist.com/auth/api)",
version: "0.0.1",
type: "action",
props: {
checkvist,
listId: {
propDefinition: [
checkvist,
"listId",
],
},
itemsContent: {
type: "string",
label: "Content Items",
description: "list items in the same format, as supported by [Checkvist's import function](https://checkvist.com/help#import).",
},
parentId: {
propDefinition: [
checkvist,
"parentId",
({ listId }) => ({
listId,
}),
],
optional: true,
},
position: {
type: "integer",
label: "Position",
description: "1-based position of the task (omit to add to the end of the list).",
optional: true,
},
parseTasks: {
type: "boolean",
label: "Parse Tasks",
description: "If true, recognize **^due** and **#tags** syntax in imported list items",
},
separateWithEmptyLine: {
type: "string",
label: "Separate With Empty Line",
description: "Select value for List items separator.",
options: SEPARATE_LINE_OPTIONS,
},
status: {
type: "string",
label: "Status",
description: "Task status",
options: STATUS_OPTIONS,
optional: true,
},
},
async run({ $ }) {
const response = await this.checkvist.createMultipleListItems({
$,
listId: this.listId,
data: {
import_content: this.itemsContent,
parent_id: this.parentId,
position: this.position,
parse_tasks: this.parseTasks,
separate_with_empty_line: this.separateWithEmptyLine,
status: this.status,
},
});

$.export("$summary", "Successfully created multiple list items");
return response;
},
Comment on lines +60 to +76
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

Enhance error handling and response details.

The run method implementation could benefit from:

  1. Adding try-catch block for better error handling
  2. Including more details in the success message (e.g., number of items created)
  3. Validating required props before making the API call

Consider applying these improvements:

   async run({ $ }) {
+    if (!this.itemsContent.trim()) {
+      throw new Error("Content Items cannot be empty");
+    }
+
+    try {
       const response = await this.checkvist.createMultipleListItems({
         $,
         listId: this.listId,
         data: {
           import_content: this.itemsContent,
           parent_id: this.parentId,
           position: this.position,
           parse_tasks: this.parseTasks,
           separate_with_empty_line: this.separateWithEmptyLine,
           status: this.status,
         },
       });
 
-      $.export("$summary", "Successfully created multiple list items");
+      const itemCount = response.length;
+      $.export("$summary", `Successfully created ${itemCount} list item${itemCount === 1 ? "" : "s"}`);
       return response;
+    } catch (error) {
+      throw new Error(`Failed to create list items: ${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.checkvist.createMultipleListItems({
$,
listId: this.listId,
data: {
import_content: this.itemsContent,
parent_id: this.parentId,
position: this.position,
parse_tasks: this.parseTasks,
separate_with_empty_line: this.separateWithEmptyLine,
status: this.status,
},
});
$.export("$summary", "Successfully created multiple list items");
return response;
},
async run({ $ }) {
if (!this.itemsContent.trim()) {
throw new Error("Content Items cannot be empty");
}
try {
const response = await this.checkvist.createMultipleListItems({
$,
listId: this.listId,
data: {
import_content: this.itemsContent,
parent_id: this.parentId,
position: this.position,
parse_tasks: this.parseTasks,
separate_with_empty_line: this.separateWithEmptyLine,
status: this.status,
},
});
const itemCount = response.length;
$.export("$summary", `Successfully created ${itemCount} list item${itemCount === 1 ? "" : "s"}`);
return response;
} catch (error) {
throw new Error(`Failed to create list items: ${error.message}`);
}
},

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

export default {
key: "checkvist-create-new-list",
name: "Create New List",
description: "Creates a new list in Checkvist. [See the documentation](https://checkvist.com/auth/api)",
version: "0.0.1",
type: "action",
props: {
checkvist,
name: {
type: "string",
label: "List Name",
description: "Name of the new list to be created",
},
public: {
type: "boolean",
label: "Public",
description: "true for checklist which can be accessed in read-only mode by anyone. Access to such checklists doesn't require authentication.",
optional: true,
},
},
async run({ $ }) {
const response = await this.checkvist.createList({
$,
data: {
name: this.name,
public: this.public,
},
});

$.export("$summary", `Successfully created a new list: ${this.name}`);
return response;
},
Comment on lines +23 to +34
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 sanitization.

The current implementation should handle potential API errors and sanitize inputs before sending to the API.

Consider implementing these improvements:

   async run({ $ }) {
+    // Sanitize inputs
+    const name = this.name.trim();
+    
+    // Validate required fields
+    if (!name) {
+      throw new Error("List name cannot be empty");
+    }
+
+    try {
       const response = await this.checkvist.createList({
         $,
         data: {
-          name: this.name,
+          name,
           public: this.public,
         },
       });
 
       $.export("$summary", `Successfully created a new list: ${this.name}`);
       return response;
+    } catch (error) {
+      throw new Error(`Failed to create list: ${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.checkvist.createList({
$,
data: {
name: this.name,
public: this.public,
},
});
$.export("$summary", `Successfully created a new list: ${this.name}`);
return response;
},
async run({ $ }) {
// Sanitize inputs
const name = this.name.trim();
// Validate required fields
if (!name) {
throw new Error("List name cannot be empty");
}
try {
const response = await this.checkvist.createList({
$,
data: {
name,
public: this.public,
},
});
$.export("$summary", `Successfully created a new list: ${this.name}`);
return response;
} catch (error) {
throw new Error(`Failed to create list: ${error.message}`);
}
},

};
98 changes: 94 additions & 4 deletions components/checkvist/checkvist.app.mjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,101 @@
import { axios } from "@pipedream/platform";

export default {
type: "app",
app: "checkvist",
propDefinitions: {},
propDefinitions: {
listId: {
type: "string",
label: "List ID",
description: "Select a list to monitor for new items",
async options() {
const lists = await this.getLists({
params: {
skip_stats: true,
},
});
return lists.map(({
id: value, name: label,
}) => ({
label,
value,
}));
},
},
parentId: {
type: "string",
label: "Parent task ID",
description: "Empty for root-level tasks",
async options({ listId }) {
const items = await this.getListItems({
listId,
});
Comment on lines +30 to +32
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

Handle empty listId in parentId options method

In the parentId prop definition, if listId is not provided, the getListItems method may fail. Consider adding a check to handle this scenario.

Here's how you might adjust the code:

    async options({ listId }) {
+     if (!listId) {
+       return [];
+     }
      const items = await this.getListItems({
        listId,
      });
📝 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
const items = await this.getListItems({
listId,
});
if (!listId) {
return [];
}
const items = await this.getListItems({
listId,
});

return items.map(({
id: value, content: label,
}) => ({
label,
value,
}));
},
},
},
methods: {
// this.$auth contains connected account data
authKeys() {
console.log(Object.keys(this.$auth));
_baseUrl() {
return "https://checkvist.com";
},
_auth() {
return {
username: `${this.$auth.username}`,
password: `${this.$auth.api_key}`,
Comment on lines +48 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

Simplify property assignments in the _auth method

In the _auth method, you can assign username and password directly without using template literals, as they are already strings.

Here's the suggested change:

    return {
-     username: `${this.$auth.username}`,
-     password: `${this.$auth.api_key}`,
+     username: this.$auth.username,
+     password: this.$auth.api_key,
    };
📝 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
username: `${this.$auth.username}`,
password: `${this.$auth.api_key}`,
username: this.$auth.username,
password: this.$auth.api_key,

};
},
_makeRequest({
$ = this, path, ...opts
}) {
return axios($, {
url: this._baseUrl() + path,
auth: this._auth(),
...opts,
});
},
getLists(opts = {}) {
return this._makeRequest({
path: "/checklists.json",
...opts,
});
},
getListItems({
listId, ...opts
}) {
return this._makeRequest({
path: `/checklists/${listId}/tasks.json`,
...opts,
});
},
createList(opts = {}) {
return this._makeRequest({
method: "POST",
path: "/checklists.json",
...opts,
});
},
createListItem({
listId, ...opts
}) {
return this._makeRequest({
method: "POST",
path: `/checklists/${listId}/tasks.json`,
...opts,
});
},
createMultipleListItems({
listId, ...opts
}) {
return this._makeRequest({
method: "POST",
path: `/checklists/${listId}/import.json`,
...opts,
});
},
},
};
25 changes: 25 additions & 0 deletions components/checkvist/common/constants.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const STATUS_OPTIONS = [
{
label: "Open",
value: "0",
},
{
label: "Closed",
value: "1",
},
{
label: "Invalidated",
value: "2",
},
];

export const SEPARATE_LINE_OPTIONS = [
{
label: "Separate with empty line",
value: "singleItem",
},
{
label: "One item per line",
value: "multipleItems",
},
];
24 changes: 24 additions & 0 deletions components/checkvist/common/utils.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export const parseObject = (obj) => {
if (!obj) return undefined;

if (Array.isArray(obj)) {
return obj.map((item) => {
if (typeof item === "string") {
try {
return JSON.parse(item);
} catch (e) {
return item;
}
}
return item;
});
}
if (typeof obj === "string") {
try {
return JSON.parse(obj);
} catch (e) {
return obj;
}
}
return obj;
};
19 changes: 19 additions & 0 deletions components/checkvist/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "@pipedream/checkvist",
"version": "0.1.0",
"description": "Pipedream Checkvist Components",
"main": "checkvist.app.mjs",
"keywords": [
"pipedream",
"checkvist"
],
"homepage": "https://pipedream.com/apps/checkvist",
"author": "Pipedream <[email protected]> (https://pipedream.com/)",
"gitHead": "e12480b94cc03bed4808ebc6b13e7fdb3a1ba535",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@pipedream/platform": "^3.0.3"
}
}
Loading
Loading