-
Notifications
You must be signed in to change notification settings - Fork 5.6k
[Components] CsvBox new components #18942
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,119 @@ | ||||||||||||||||||
| import FormData from "form-data"; | ||||||||||||||||||
| import { getFileStream } from "@pipedream/platform"; | ||||||||||||||||||
| import app from "../../csvbox.app.mjs"; | ||||||||||||||||||
|
|
||||||||||||||||||
| export default { | ||||||||||||||||||
| key: "csvbox-submit-spreadsheet", | ||||||||||||||||||
| name: "Submit Spreadsheet", | ||||||||||||||||||
| description: "Submit a spreadsheet file via public URL or local file path to CSVBox for processing. [See documentation](https://help.csvbox.io/advanced-installation/rest-file-api)", | ||||||||||||||||||
| version: "0.0.1", | ||||||||||||||||||
| type: "action", | ||||||||||||||||||
| props: { | ||||||||||||||||||
| app, | ||||||||||||||||||
| file: { | ||||||||||||||||||
| type: "string", | ||||||||||||||||||
| label: "File Path Or URL", | ||||||||||||||||||
| description: "Provide either a file URL or a path to a file in the `/tmp` directory (for example, `/tmp/spreadsheet.csv`).", | ||||||||||||||||||
| }, | ||||||||||||||||||
| sheetLicenseKey: { | ||||||||||||||||||
| propDefinition: [ | ||||||||||||||||||
| app, | ||||||||||||||||||
| "sheetLicenseKey", | ||||||||||||||||||
| ], | ||||||||||||||||||
| }, | ||||||||||||||||||
| userId: { | ||||||||||||||||||
| propDefinition: [ | ||||||||||||||||||
| app, | ||||||||||||||||||
| "userId", | ||||||||||||||||||
| ], | ||||||||||||||||||
| }, | ||||||||||||||||||
| hasHeaders: { | ||||||||||||||||||
| propDefinition: [ | ||||||||||||||||||
| app, | ||||||||||||||||||
| "hasHeaders", | ||||||||||||||||||
| ], | ||||||||||||||||||
| }, | ||||||||||||||||||
| syncDir: { | ||||||||||||||||||
| type: "dir", | ||||||||||||||||||
| accessMode: "read", | ||||||||||||||||||
| sync: true, | ||||||||||||||||||
| optional: true, | ||||||||||||||||||
| }, | ||||||||||||||||||
| }, | ||||||||||||||||||
| annotations: { | ||||||||||||||||||
| readOnlyHint: false, | ||||||||||||||||||
| destructiveHint: false, | ||||||||||||||||||
| openWorldHint: true, | ||||||||||||||||||
| }, | ||||||||||||||||||
| methods: { | ||||||||||||||||||
| booleanToNumber(value) { | ||||||||||||||||||
| return value === true || value === "true" || value === "1" || value === 1 | ||||||||||||||||||
| ? 1 | ||||||||||||||||||
| : 0; | ||||||||||||||||||
| }, | ||||||||||||||||||
| }, | ||||||||||||||||||
| async run({ $ }) { | ||||||||||||||||||
| const { | ||||||||||||||||||
| app, | ||||||||||||||||||
| booleanToNumber, | ||||||||||||||||||
| file, | ||||||||||||||||||
| sheetLicenseKey, | ||||||||||||||||||
| userId, | ||||||||||||||||||
| hasHeaders, | ||||||||||||||||||
| } = this; | ||||||||||||||||||
| let data; | ||||||||||||||||||
|
|
||||||||||||||||||
| const isUrl = file?.startsWith("http://") || file?.startsWith("https://"); | ||||||||||||||||||
|
|
||||||||||||||||||
| const otherFields = { | ||||||||||||||||||
| ...(userId | ||||||||||||||||||
| ? { | ||||||||||||||||||
| user: { | ||||||||||||||||||
| user_id: userId, | ||||||||||||||||||
| }, | ||||||||||||||||||
| } | ||||||||||||||||||
| : {} | ||||||||||||||||||
| ), | ||||||||||||||||||
| ...(hasHeaders | ||||||||||||||||||
| ? { | ||||||||||||||||||
| options: { | ||||||||||||||||||
| has_header: booleanToNumber(hasHeaders), | ||||||||||||||||||
| }, | ||||||||||||||||||
| } | ||||||||||||||||||
| : {} | ||||||||||||||||||
| ), | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| if (isUrl) { | ||||||||||||||||||
| data = { | ||||||||||||||||||
| import: { | ||||||||||||||||||
| public_file_url: file, | ||||||||||||||||||
| sheet_license_key: sheetLicenseKey, | ||||||||||||||||||
| ...otherFields, | ||||||||||||||||||
| }, | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
| } else { | ||||||||||||||||||
| data = new FormData(); | ||||||||||||||||||
| data.append("file", await getFileStream(file)); | ||||||||||||||||||
| data.append("import", JSON.stringify({ | ||||||||||||||||||
| sheet_license_key: sheetLicenseKey, | ||||||||||||||||||
| ...otherFields, | ||||||||||||||||||
| })); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| const response = await app.submitFile({ | ||||||||||||||||||
| $, | ||||||||||||||||||
| headers: !isUrl | ||||||||||||||||||
| ? { | ||||||||||||||||||
| "Content-Type": "multipart/form-data", | ||||||||||||||||||
| } | ||||||||||||||||||
| : undefined, | ||||||||||||||||||
|
Comment on lines
+107
to
+111
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix multipart Content-Type to include boundary. Manually setting Apply this diff: headers: !isUrl
- ? {
- "Content-Type": "multipart/form-data",
- }
+ ? data.getHeaders()
: undefined,This will properly include the boundary parameter in the Content-Type header (e.g., 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| data, | ||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| $.export("$summary", "Successfully submitted spreadsheet"); | ||||||||||||||||||
|
|
||||||||||||||||||
| return response; | ||||||||||||||||||
| }, | ||||||||||||||||||
| }; | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,55 @@ | ||
| import { axios } from "@pipedream/platform"; | ||
|
|
||
| export default { | ||
| type: "app", | ||
| app: "csvbox", | ||
| propDefinitions: {}, | ||
| propDefinitions: { | ||
| sheetLicenseKey: { | ||
| type: "string", | ||
| label: "Sheet License Key", | ||
| description: "The unique identifier for your CSVBox sheet. You can find it in **Sheets - Edit - Code Snippet - Sheet License Key**.", | ||
| }, | ||
| userId: { | ||
| type: "string", | ||
| label: "User ID", | ||
| description: "The unique identifier for the user. You can find it in the **Dashboard - Edit - Code Snippet**.", | ||
| optional: true, | ||
| }, | ||
| hasHeaders: { | ||
| type: "boolean", | ||
| label: "Has Headers", | ||
| description: "Whether the spreadsheet has headers.", | ||
| optional: true, | ||
| }, | ||
| }, | ||
| methods: { | ||
| // this.$auth contains connected account data | ||
| authKeys() { | ||
| console.log(Object.keys(this.$auth)); | ||
| getUrl(path) { | ||
| return `https://api.csvbox.io/1.1${path}`; | ||
| }, | ||
| getHeaders(headers) { | ||
| return { | ||
| "Content-Type": "application/json", | ||
| "x-csvbox-api-key": `${this.$auth.api_key}`, | ||
| "x-csvbox-secret-api-key": `${this.$auth.secret_api_key}`, | ||
| ...headers, | ||
| }; | ||
| }, | ||
| _makeRequest({ | ||
| $ = this, path, headers, ...args | ||
| } = {}) { | ||
| return axios($, { | ||
| debug: true, | ||
| url: this.getUrl(path), | ||
| headers: this.getHeaders(headers), | ||
| ...args, | ||
| }); | ||
| }, | ||
| submitFile(args = {}) { | ||
| return this._makeRequest({ | ||
| method: "POST", | ||
| path: "/file", | ||
| ...args, | ||
| }); | ||
| }, | ||
| }, | ||
| }; | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,33 @@ | ||||||||||||||
| import { ConfigurationError } from "@pipedream/platform"; | ||||||||||||||
| import app from "../../csvbox.app.mjs"; | ||||||||||||||
| import sampleEvent from "./test-event.mjs"; | ||||||||||||||
|
|
||||||||||||||
| export default { | ||||||||||||||
| key: "csvbox-new-import", | ||||||||||||||
| name: "New Import", | ||||||||||||||
| description: "Emit new event when CSVBox receives and processes a new import. [See documentation](https://help.csvbox.io/destinations#api-webhook)", | ||||||||||||||
| version: "0.0.1", | ||||||||||||||
| type: "source", | ||||||||||||||
| dedupe: "unique", | ||||||||||||||
| props: { | ||||||||||||||
| app, | ||||||||||||||
| http: "$.interface.http", | ||||||||||||||
| }, | ||||||||||||||
| async run({ | ||||||||||||||
| headers, body, | ||||||||||||||
| }) { | ||||||||||||||
|
|
||||||||||||||
| if (!headers["content-type"] || headers["content-type"] !== "application/json") { | ||||||||||||||
| throw new ConfigurationError("Invalid content type. Please check your CSVBox webhook configuration so that the content type is set to `JSON`."); | ||||||||||||||
| } | ||||||||||||||
|
Comment on lines
+20
to
+22
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Content-Type validation is too strict. The exact equality check Apply this diff to use a more flexible check: - if (!headers["content-type"] || headers["content-type"] !== "application/json") {
+ if (!headers["content-type"]?.startsWith("application/json")) {
throw new ConfigurationError("Invalid content type. Please check your CSVBox webhook configuration so that the content type is set to `JSON`.");
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
|
|
||||||||||||||
| const ts = Date.now(); | ||||||||||||||
|
|
||||||||||||||
| this.$emit(body, { | ||||||||||||||
| id: ts, | ||||||||||||||
| summary: "New import has been processed", | ||||||||||||||
| ts, | ||||||||||||||
| }); | ||||||||||||||
| }, | ||||||||||||||
| sampleEvent, | ||||||||||||||
| }; | ||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,97 @@ | ||||||
| export default [ | ||||||
| { | ||||||
| "import_id": 1841488, | ||||||
| "sheet_id": 19247, | ||||||
| "sheet_name": "Test 100", | ||||||
| "row_number": 1, | ||||||
| "total_rows": 5, | ||||||
| "env_name": "default", | ||||||
| "original_filename": "basicScience.csv", | ||||||
| "custom_fields": [], | ||||||
| "row_data": { | ||||||
| "ID": "2", | ||||||
| "Question": "What type of paper detects acids and alkali in liquid?", | ||||||
| "Answer": "Litmus Paper", | ||||||
| "Option B": "Filter paper", | ||||||
| "Option C": "Hydrion Paper", | ||||||
| "Option D": "Proton Paper" | ||||||
| }, | ||||||
| "import_description": "" | ||||||
| }, | ||||||
| { | ||||||
| "import_id": 1841488, | ||||||
| "sheet_id": 19247, | ||||||
| "sheet_name": "Test 100", | ||||||
| "row_number": 2, | ||||||
| "total_rows": 5, | ||||||
| "env_name": "default", | ||||||
| "original_filename": "basicScience.csv", | ||||||
| "custom_fields": [], | ||||||
| "row_data": { | ||||||
| "ID": "3", | ||||||
| "Question": "What is the only metal to be liquid at room temperature? ", | ||||||
| "Answer": "Mercury", | ||||||
| "Option B": "Nickel", | ||||||
| "Option C": "Chromium", | ||||||
| "Option D": "Zirconium" | ||||||
| }, | ||||||
| "import_description": "" | ||||||
| }, | ||||||
| { | ||||||
| "import_id": 1841488, | ||||||
| "sheet_id": 19247, | ||||||
| "sheet_name": "Test 100", | ||||||
| "row_number": 3, | ||||||
| "total_rows": 5, | ||||||
| "env_name": "default", | ||||||
| "original_filename": "basicScience.csv", | ||||||
| "custom_fields": [], | ||||||
| "row_data": { | ||||||
| "ID": "4", | ||||||
| "Question": "What is the chemical name of aqua fortis?", | ||||||
| "Answer": "Nitric Acid", | ||||||
| "Option B": "Hydrochloric Acid", | ||||||
| "Option C": "Benzoic Acid", | ||||||
| "Option D": "Acetic Acid" | ||||||
| }, | ||||||
| "import_description": "" | ||||||
| }, | ||||||
| { | ||||||
| "import_id": 1841488, | ||||||
| "sheet_id": 19247, | ||||||
| "sheet_name": "Test 100", | ||||||
| "row_number": 4, | ||||||
| "total_rows": 5, | ||||||
| "env_name": "default", | ||||||
| "original_filename": "basicScience.csv", | ||||||
| "custom_fields": [], | ||||||
| "row_data": { | ||||||
| "ID": "5", | ||||||
| "Question": "What is the fourth state of matter after solid and liquid and gas?", | ||||||
| "Answer": "Plasma", | ||||||
| "Option B": "Ground State", | ||||||
| "Option C": "Metal ", | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove trailing whitespace. "Metal " contains trailing spaces, which is likely unintentional. Apply this diff: - "Option C": "Metal ",
+ "Option C": "Metal",📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| "Option D": "Flora" | ||||||
| }, | ||||||
| "import_description": "" | ||||||
| }, | ||||||
| { | ||||||
| "import_id": 1841488, | ||||||
| "sheet_id": 19247, | ||||||
| "sheet_name": "Test 100", | ||||||
| "row_number": 5, | ||||||
| "total_rows": 5, | ||||||
| "env_name": "default", | ||||||
| "original_filename": "basicScience.csv", | ||||||
| "custom_fields": [], | ||||||
| "row_data": { | ||||||
| "ID": "6", | ||||||
| "Question": "What is the chemical symbol for Plutonium? ", | ||||||
| "Answer": "Pu", | ||||||
| "Option B": "Pa", | ||||||
| "Option C": "Po", | ||||||
| "Option D": "P" | ||||||
| }, | ||||||
| "import_description": "" | ||||||
| } | ||||||
| ]; | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle explicit false value for hasHeaders.
The current truthiness check means that when
hasHeadersis explicitly set tofalse, theoptionsobject won't be included in the request. This prevents users from explicitly indicating that the spreadsheet has no headers.Apply this diff to handle explicit false values:
📝 Committable suggestion
🤖 Prompt for AI Agents