mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
fix conflict
This commit is contained in:
commit
40c951fab9
46
CHANGELOG.md
46
CHANGELOG.md
|
@ -1,3 +1,47 @@
|
|||
# [0.171.0](https://github.com/n8n-io/n8n/compare/n8n@0.170.0...n8n@0.171.0) (2022-04-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** Fix crash on webhook when last node did not return data ([c50d04a](https://github.com/n8n-io/n8n/commit/c50d04af9eb033d82860c336fc7350b5c3f22242))
|
||||
* **EmailReadImap Node:** Fix issue that crashed process if node was configured wrong ([#3079](https://github.com/n8n-io/n8n/issues/3079)) ([85f15d4](https://github.com/n8n-io/n8n/commit/85f15d49896d876fa3ab84e9fa1846f856851274))
|
||||
* **Google Tasks Node:** Fix "Show Completed" option and hide title field where not needed ([#2741](https://github.com/n8n-io/n8n/issues/2741)) ([9d703e3](https://github.com/n8n-io/n8n/commit/9d703e366b8e191e0f588469892ebb7b6d03c1d3))
|
||||
* **NocoDB Node:** Fix pagination ([#3081](https://github.com/n8n-io/n8n/issues/3081)) ([5f44b0d](https://github.com/n8n-io/n8n/commit/5f44b0dad5254fe9f985b314db8f7d43ab48c712))
|
||||
* **Salesforce Node:** Fix issue that "status" did not get used for Case => Create & Update ([#2212](https://github.com/n8n-io/n8n/issues/2212)) ([1018146](https://github.com/n8n-io/n8n/commit/1018146f21c47eda9f888bd19e92d1106c49267a))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **editor:** Add download button for binary data ([#2992](https://github.com/n8n-io/n8n/issues/2992)) ([13a9db7](https://github.com/n8n-io/n8n/commit/13a9db774576a00d4e3ce1988557654d00067073))
|
||||
* **Emelia Node:** Add Campaign > Duplicate functionality ([#3000](https://github.com/n8n-io/n8n/issues/3000)) ([0b08be1](https://github.com/n8n-io/n8n/commit/0b08be1c0b2961f235fc2446a36afe3995b4d847)), closes [#3065](https://github.com/n8n-io/n8n/issues/3065) [#2741](https://github.com/n8n-io/n8n/issues/2741) [#3075](https://github.com/n8n-io/n8n/issues/3075)
|
||||
* **FTP Node:** Add option to recursively create directories on rename ([#3001](https://github.com/n8n-io/n8n/issues/3001)) ([39a6f41](https://github.com/n8n-io/n8n/commit/39a6f417203b76cfa2c68816c49e86dc7236aba4))
|
||||
* **Mautic Node:** Add credential test and allow trailing slash in host ([#3080](https://github.com/n8n-io/n8n/issues/3080)) ([0a75539](https://github.com/n8n-io/n8n/commit/0a75539cc3d696a8946d7db5ff5842ff54835134))
|
||||
* **Microsoft Teams Node:** Add chat message support ([#2635](https://github.com/n8n-io/n8n/issues/2635)) ([984f62d](https://github.com/n8n-io/n8n/commit/984f62df9ed92cdf297b3b56300c9f23bf128d2d))
|
||||
* **Mocean Node:** Add "Delivery Report URL" option and credential tests ([#3075](https://github.com/n8n-io/n8n/issues/3075)) ([c89d2b1](https://github.com/n8n-io/n8n/commit/c89d2b10f2461ff8e90209b8f29c222f9430dba5))
|
||||
* **ServiceNow Node:** Add basicAuth support and fix getColumns loadOptions ([#2712](https://github.com/n8n-io/n8n/issues/2712)) ([2c72584](https://github.com/n8n-io/n8n/commit/2c72584b55521b437baa20ddad7c919807fd9f8f)), closes [#2741](https://github.com/n8n-io/n8n/issues/2741) [#3075](https://github.com/n8n-io/n8n/issues/3075) [#3000](https://github.com/n8n-io/n8n/issues/3000) [#3065](https://github.com/n8n-io/n8n/issues/3065) [#2741](https://github.com/n8n-io/n8n/issues/2741) [#3075](https://github.com/n8n-io/n8n/issues/3075) [#3071](https://github.com/n8n-io/n8n/issues/3071) [#3001](https://github.com/n8n-io/n8n/issues/3001) [#2635](https://github.com/n8n-io/n8n/issues/2635) [#3080](https://github.com/n8n-io/n8n/issues/3080) [#3061](https://github.com/n8n-io/n8n/issues/3061) [#3081](https://github.com/n8n-io/n8n/issues/3081) [#2582](https://github.com/n8n-io/n8n/issues/2582) [#2212](https://github.com/n8n-io/n8n/issues/2212)
|
||||
* **Strava Node:** Add "Get Streams" operation ([#2582](https://github.com/n8n-io/n8n/issues/2582)) ([6bbb4df](https://github.com/n8n-io/n8n/commit/6bbb4df05925362404f844a23a695f186d27b72e))
|
||||
|
||||
|
||||
|
||||
# [0.170.0](https://github.com/n8n-io/n8n/compare/n8n@0.169.0...n8n@0.170.0) (2022-03-27)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **core:** Add logs and error catches for possible failures in queue mode ([#3032](https://github.com/n8n-io/n8n/issues/3032)) ([3b4a97d](https://github.com/n8n-io/n8n/commit/3b4a97dd576bd3c2f53f958266964d3e02f01c96))
|
||||
- **AWS Lambda Node:** Fix "Invocation Type" > "Continue Workflow" ([#3010](https://github.com/n8n-io/n8n/issues/3010)) ([9547a08](https://github.com/n8n-io/n8n/commit/9547a08f0344825e42f5580da035bb1f21c03368))
|
||||
- **Supabase Node:** Fix Row > Get operation ([#3045](https://github.com/n8n-io/n8n/issues/3045)) ([b9aa440](https://github.com/n8n-io/n8n/commit/b9aa440be3d52bf412990b93cfc3758353fb4943))
|
||||
- **Supabase Node:** Send token also via Authorization Bearer ([#2814](https://github.com/n8n-io/n8n/issues/2814)) ([5774dd8](https://github.com/n8n-io/n8n/commit/5774dd8885a87a1ebe70f4ef4a06a42013112afe))
|
||||
- **Xero Node:** Fix some operations and add support for setting address and phone number ([#3048](https://github.com/n8n-io/n8n/issues/3048)) ([ab08c0d](https://github.com/n8n-io/n8n/commit/ab08c0df1599d44326b45c37f80918e5c107cc6a))
|
||||
- **Wise Node:** Fix issue when executing a transfer ([#3039](https://github.com/n8n-io/n8n/issues/3039)) ([b90bf45](https://github.com/n8n-io/n8n/commit/b90bf4576c6e3f86000d61606f412ea0544b59ef))
|
||||
|
||||
### Features
|
||||
|
||||
- **Crypto Node:** Add Generate operation to generate random values ([#2541](https://github.com/n8n-io/n8n/issues/2541)) ([b5ecccb](https://github.com/n8n-io/n8n/commit/b5ecccb84080362880a307e3f9d76d429bd1d537))
|
||||
- **HTTP Request Node:** Add support for OPTIONS method ([#3030](https://github.com/n8n-io/n8n/issues/3030)) ([bd9064c](https://github.com/n8n-io/n8n/commit/bd9064cd0ea8833b49a7e3860f12bfa37c286947))
|
||||
- **Jira Node:** Add Simplify Output option to Issue > Get ([#2408](https://github.com/n8n-io/n8n/issues/2408)) ([016aeaa](https://github.com/n8n-io/n8n/commit/016aeaaa791205c5ee3d16eef25f856603cf0085))
|
||||
- **Reddit Node:** Add possibility to query saved posts ([#3034](https://github.com/n8n-io/n8n/issues/3034)) ([5ba4c27](https://github.com/n8n-io/n8n/commit/5ba4c27d8c417964187af89a15d5dd4ce9f3271a))
|
||||
- **Zendesk Node:** Add ticket status "On-hold" ([2b20a46](https://github.com/n8n-io/n8n/commit/2b20a460915655791647d62b48dde97dad3b2fd3))
|
||||
|
||||
# [0.169.0](https://github.com/n8n-io/n8n/compare/n8n@0.168.2...n8n@0.169.0) (2022-03-20)
|
||||
|
||||
### License change
|
||||
|
@ -55,3 +99,5 @@ From [Apache 2.0 with Commons Clause](https://github.com/n8n-io/n8n/blob/181ba3c
|
|||
- **MongoDb Node:** Add Aggregate Operation ([2c9a06e](https://github.com/n8n-io/n8n/commit/2c9a06e86346a9e21f877cb508d13a1401c700a9))
|
||||
- **Redis Node:** Add Redis Trigger node and publish operation to regular node ([5c2deb4](https://github.com/n8n-io/n8n/commit/5c2deb468867ec77a05d09ef324d4855210e17d4))
|
||||
- **Wordpress Node:** Add Status option to Get All operation of Posts resource ([4d4db7f](https://github.com/n8n-io/n8n/commit/4d4db7f805673758dfb379c9e86e98815f265db2))
|
||||
|
||||
> **Note:** for changelogs before 0.167.0, refer to the [Release notes](https://docs.n8n.io/reference/release-notes.html) in the documentation.
|
||||
|
|
|
@ -12,7 +12,7 @@ The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensa
|
|||
|
||||
## Limitations
|
||||
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use.
|
||||
You may distribute the software or provide it to others only if you do so free of charge for non-commercial purposes.
|
||||
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
||||
|
||||
|
|
69911
package-lock.json
generated
69911
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -2,13 +2,27 @@
|
|||
|
||||
This list shows all the versions which include breaking changes and how to upgrade.
|
||||
|
||||
## 0.171.0
|
||||
|
||||
### What changed?
|
||||
|
||||
The GraphQL node now errors when the response includes an error.
|
||||
|
||||
### When is action necessary?
|
||||
|
||||
If you are using the GraphQL node.
|
||||
|
||||
### How to upgrade:
|
||||
|
||||
Go to the workflows that use the GraphQL node and adjust them to the new behavior. If you want to continue even on error, you can set "Continue on Fail" to true.
|
||||
|
||||
## 0.165.0
|
||||
|
||||
### What changed?
|
||||
|
||||
The Hive node now correctly rejects invalid SSL certificates when the "Ignore SSL Issues" option is set to False.
|
||||
|
||||
### When is action necassary?
|
||||
### When is action necessary?
|
||||
|
||||
If you are using a self signed certificate with The Hive.
|
||||
|
||||
|
@ -28,7 +42,7 @@ If you are using the Hubspot Trigger.
|
|||
|
||||
### How to upgrade:
|
||||
|
||||
Create an app in HubSpot, use the Client ID, Client Secret, App ID, and the Developer Key, and complete the OAuth2 flow.
|
||||
Create an app in HubSpot, use the Client ID, Client Secret, App ID, and the Developer Key, and complete the OAuth2 flow.
|
||||
|
||||
## 0.135.0
|
||||
|
||||
|
@ -59,15 +73,14 @@ const credentials = await this.getCredentials(myNodeCredentials);
|
|||
Example:
|
||||
|
||||
```typescript
|
||||
|
||||
const items = this.getInputData();
|
||||
|
||||
for (const i = 0; i < items.length; i++) {
|
||||
const item = items[i].binary as IBinaryKeyData;
|
||||
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string;
|
||||
const binaryData = item[binaryPropertyName] as IBinaryData;
|
||||
const item = items[i].binary as IBinaryKeyData;
|
||||
const binaryPropertyName = this.getNodeParameter('binaryPropertyName', i) as string;
|
||||
const binaryData = item[binaryPropertyName] as IBinaryData;
|
||||
// Before 0.135.0:
|
||||
const binaryDataBuffer = Buffer.from(binaryData.data, BINARY_ENCODING);
|
||||
const binaryDataBuffer = Buffer.from(binaryData.data, BINARY_ENCODING);
|
||||
// From 0.135.0:
|
||||
const binaryDataBuffer = await this.helpers.getBinaryDataBuffer(i, binaryPropertyName);
|
||||
}
|
||||
|
@ -106,17 +119,21 @@ If you are using `lead:create` with "Company" or "Address", reset the parameters
|
|||
## 0.118.0
|
||||
|
||||
### What changed?
|
||||
|
||||
The minimum Node.js version required for n8n is now v14.
|
||||
|
||||
### When is action necessary?
|
||||
|
||||
If you're using n8n via npm or PM2 or if you're contributing to n8n.
|
||||
|
||||
### How to upgrade:
|
||||
|
||||
Update the Node.js version to v14 or above.
|
||||
|
||||
----------------------------
|
||||
---
|
||||
|
||||
### What changed?
|
||||
|
||||
In the Postgres, CrateDB, QuestDB and TimescaleDB nodes the `Execute Query` operation returns the result from all queries executed instead of just one of the results.
|
||||
|
||||
### When is action necessary?
|
||||
|
@ -126,6 +143,7 @@ If you use any of the above mentioned nodes with the `Execute Query` operation a
|
|||
## 0.117.0
|
||||
|
||||
### What changed?
|
||||
|
||||
Removed the "Activation Trigger" node. This node was replaced by two other nodes.
|
||||
|
||||
The "Activation Trigger" node was added on version 0.113.0 but was not fully compliant to UX, so we decided to refactor and change it ASAP so it affects the least possible users.
|
||||
|
@ -140,7 +158,7 @@ If you use the "Activation Trigger" in any of your workflows, please replace it
|
|||
|
||||
Remove the previous node and add the new ones according to your workflows.
|
||||
|
||||
----------------------------
|
||||
---
|
||||
|
||||
Changed the behavior for nodes that use Postgres Wire Protocol: Postgres, QuestDB, CrateDB and TimescaleDB.
|
||||
|
||||
|
@ -158,10 +176,10 @@ By default, all `insert` operations will have `Return fields: *` as the default,
|
|||
|
||||
Previously, the node would return all information it received, without taking into account what actually happened in the database.
|
||||
|
||||
|
||||
## 0.113.0
|
||||
|
||||
### What changed?
|
||||
|
||||
In the Dropbox node, both credential types (Access Token & OAuth2) have a new parameter called "APP Access Type".
|
||||
|
||||
### When is action necessary?
|
||||
|
@ -175,6 +193,7 @@ Open your Dropbox node's credentials and set the "APP Access Type" parameter to
|
|||
## 0.111.0
|
||||
|
||||
### What changed?
|
||||
|
||||
In the Dropbox node, now all operations are performed relative to the user's root directory.
|
||||
|
||||
### When is action necessary?
|
||||
|
@ -192,24 +211,29 @@ Also, if you are using the `folder:list` operation, make sure your logic is taki
|
|||
## 0.105.0
|
||||
|
||||
### What changed?
|
||||
|
||||
In the Hubspot Trigger, now multiple events can be provided and the field `App ID` was so moved to the credentials.
|
||||
|
||||
### When is action necessary?
|
||||
|
||||
If you are using the Hubspot Trigger node.
|
||||
|
||||
### How to upgrade:
|
||||
Open the Hubspot Trigger and set the events again. Also open the credentials `Hubspot Developer API` and set your APP ID.
|
||||
|
||||
Open the Hubspot Trigger and set the events again. Also open the credentials `Hubspot Developer API` and set your APP ID.
|
||||
|
||||
## 0.104.0
|
||||
|
||||
### What changed?
|
||||
|
||||
Support for MongoDB as a database for n8n has been dropped as MongoDB had problems saving large amounts of data in a document, among other issues.
|
||||
|
||||
### When is action necessary?
|
||||
|
||||
If you have been using MongoDB as a database for n8n. Please note that this is not related to the MongoDB node.
|
||||
|
||||
### How to upgrade:
|
||||
|
||||
Before upgrading, you can [export](https://docs.n8n.io/reference/start-workflows-via-cli.html#export-workflows-and-credentials) all your credentials and workflows using the CLI.
|
||||
|
||||
```
|
||||
|
@ -227,21 +251,26 @@ n8n import:credentials --separate --input=backups/latest/
|
|||
## 0.102.0
|
||||
|
||||
### What changed?
|
||||
- The `As User` property and the `User Name` field got combined and renamed to `Send as User`. It also got moved under “Add Options”.
|
||||
|
||||
- The `As User` property and the `User Name` field got combined and renamed to `Send as User`. It also got moved under “Add Options”.
|
||||
- The `Ephemeral` property got removed. To send an ephemeral message, you have to select the "Post (Ephemeral)" operation.
|
||||
|
||||
### When is action necessary?
|
||||
|
||||
If you are using the following fields or properties in the Slack node:
|
||||
|
||||
- As User
|
||||
- Ephemeral
|
||||
- User Name
|
||||
|
||||
### How to upgrade:
|
||||
|
||||
Open the Slack node and set them again to the appropriate values.
|
||||
|
||||
----------------------------
|
||||
---
|
||||
|
||||
### What changed?
|
||||
|
||||
If you have a question in Typeform that uses a previously answered question as part of its text, the question text would look like this in the Typeform Trigger node:
|
||||
|
||||
`You have chosen {{field:23234242}} as your answer. Is this correct?`
|
||||
|
@ -251,9 +280,11 @@ Those curly braces broke the expression editor. The change makes it now display
|
|||
`You have chosen [field:23234242] as your answer. Is this correct?`
|
||||
|
||||
### When is action necessary?
|
||||
|
||||
If you are using the Typeform Trigger node with questions using the [Recall information](https://help.typeform.com/hc/en-us/articles/360050447072-What-is-Recall-information-) feature.
|
||||
|
||||
### How to upgrade:
|
||||
|
||||
In workflows using the Typeform Trigger node, nodes that reference such key names (questions that use a previously answered question as part of its text) will need to be updated.
|
||||
|
||||
## 0.95.0
|
||||
|
@ -281,11 +312,11 @@ In the Segment Node, we have changed how the properties 'traits' and 'properties
|
|||
When the properties 'traits' or 'properties' are set, and one of the following resources/operations is used:
|
||||
|
||||
| Resource | Operation |
|
||||
|--|--|
|
||||
| Identify | Create |
|
||||
| Track | Event |
|
||||
| Track | Page |
|
||||
| Group | Add |
|
||||
| -------- | --------- |
|
||||
| Identify | Create |
|
||||
| Track | Event |
|
||||
| Track | Page |
|
||||
| Group | Add |
|
||||
|
||||
### How to upgrade:
|
||||
|
||||
|
@ -305,7 +336,6 @@ If you had set "Basic Auth" for the "Authentication" field in the node.
|
|||
|
||||
The "Authentication" field has been renamed to "Incoming Authentication". Please set the parameter “Incoming Authentication” to “Basic Auth” to activate it again.
|
||||
|
||||
|
||||
## 0.90.0
|
||||
|
||||
### What changed?
|
||||
|
@ -320,7 +350,6 @@ If you are running Node.js version older than 12.9.
|
|||
|
||||
You can find download and install the latest version of Node.js from [here](https://nodejs.org/en/download/).
|
||||
|
||||
|
||||
## 0.87.0
|
||||
|
||||
### What changed?
|
||||
|
@ -335,7 +364,6 @@ If you are are actively using the link.fish node.
|
|||
|
||||
Unfortunately, that's not possible. We'd recommend you to look for an alternative service.
|
||||
|
||||
|
||||
## 0.83.0
|
||||
|
||||
### What changed?
|
||||
|
@ -346,13 +374,13 @@ In the Active Campaign Node, we have changed how the `getAll` operation works wi
|
|||
|
||||
When one of the following resources/operations is used:
|
||||
|
||||
| Resource | Operation |
|
||||
|--|--|
|
||||
| Deal | Get All |
|
||||
| Connector | Get All |
|
||||
| E-commerce Order | Get All |
|
||||
| E-commerce Customer | Get All |
|
||||
| E-commerce Order Products | Get All |
|
||||
| Resource | Operation |
|
||||
| ------------------------- | --------- |
|
||||
| Deal | Get All |
|
||||
| Connector | Get All |
|
||||
| E-commerce Order | Get All |
|
||||
| E-commerce Customer | Get All |
|
||||
| E-commerce Order Products | Get All |
|
||||
|
||||
### How to upgrade:
|
||||
|
||||
|
@ -393,7 +421,6 @@ If you have used the Attachments option in your Twitter nodes.
|
|||
|
||||
You'll need to re-create the attachments for the Twitter node.
|
||||
|
||||
|
||||
## 0.68.0
|
||||
|
||||
### What changed?
|
||||
|
@ -413,36 +440,39 @@ All values that get referenced which were before under the property "channel" ar
|
|||
This means that these expressions have to get adjusted.
|
||||
|
||||
Meaning if the expression used before was:
|
||||
|
||||
```
|
||||
{{ $node["Slack"].data["channel"]["id"] }}
|
||||
```
|
||||
|
||||
it has to get changed to:
|
||||
|
||||
```
|
||||
{{ $node["Slack"].data["id"] }}
|
||||
```
|
||||
|
||||
|
||||
## 0.67.0
|
||||
|
||||
### What changed?
|
||||
|
||||
The names of the following nodes were not set correctly and got fixed:
|
||||
- AMQP Sender
|
||||
- Bitbucket-Trigger
|
||||
- Coda
|
||||
- Eventbrite-Trigger
|
||||
- Flow
|
||||
- Flow-Trigger
|
||||
- Gumroad-Trigger
|
||||
- Jira
|
||||
- Mailchimp-Trigger
|
||||
- PayPal Trigger
|
||||
- Read PDF
|
||||
- Rocketchat
|
||||
- Shopify
|
||||
- Shopify-Trigger
|
||||
- Stripe-Trigger
|
||||
- Toggl-Trigger
|
||||
|
||||
- AMQP Sender
|
||||
- Bitbucket-Trigger
|
||||
- Coda
|
||||
- Eventbrite-Trigger
|
||||
- Flow
|
||||
- Flow-Trigger
|
||||
- Gumroad-Trigger
|
||||
- Jira
|
||||
- Mailchimp-Trigger
|
||||
- PayPal Trigger
|
||||
- Read PDF
|
||||
- Rocketchat
|
||||
- Shopify
|
||||
- Shopify-Trigger
|
||||
- Stripe-Trigger
|
||||
- Toggl-Trigger
|
||||
|
||||
### When is action necessary?
|
||||
|
||||
|
@ -454,32 +484,32 @@ For the nodes mentioned above, you'll need to give them access to the credential
|
|||
|
||||
**Simple**
|
||||
|
||||
- Note down the settings of the nodes before upgrading
|
||||
- After upgrading, delete the nodes mentioned above from your workflow, and recreate them
|
||||
- Note down the settings of the nodes before upgrading
|
||||
- After upgrading, delete the nodes mentioned above from your workflow, and recreate them
|
||||
|
||||
**Advanced**
|
||||
|
||||
After upgrading, select the whole workflow in the editor, copy it, and paste it into a text editor. In the JSON, change the node types manually by replacing the values for "type" as follows:
|
||||
- "n8n-nodes-base.amqpSender" -> "n8n-nodes-base.amqp"
|
||||
- "n8n-nodes-base.bitbucket" -> "n8n-nodes-base.bitbucketTrigger"
|
||||
- "n8n-nodes-base.Coda" -> "n8n-nodes-base.coda"
|
||||
- "n8n-nodes-base.eventbrite" -> "n8n-nodes-base.eventbriteTrigger"
|
||||
- "n8n-nodes-base.Flow" -> "n8n-nodes-base.flow"
|
||||
- "n8n-nodes-base.flow" -> "n8n-nodes-base.flowTrigger"
|
||||
- "n8n-nodes-base.gumroad" -> "n8n-nodes-base.gumroadTrigger"
|
||||
- "n8n-nodes-base.Jira Software Cloud" -> "n8n-nodes-base.jira"
|
||||
- "n8n-nodes-base.Mailchimp" -> "n8n-nodes-base.mailchimpTrigger"
|
||||
- "n8n-nodes-base.PayPal" -> "n8n-nodes-base.payPalTrigger"
|
||||
- "n8n-nodes-base.Read PDF" -> "n8n-nodes-base.readPDF"
|
||||
- "n8n-nodes-base.Rocketchat" -> "n8n-nodes-base.rocketchat"
|
||||
- "n8n-nodes-base.shopify" -> "n8n-nodes-base.shopifyTrigger"
|
||||
- "n8n-nodes-base.shopifyNode" -> "n8n-nodes-base.shopify"
|
||||
- "n8n-nodes-base.stripe" -> "n8n-nodes-base.stripeTrigger"
|
||||
- "n8n-nodes-base.toggl" -> "n8n-nodes-base.togglTrigger"
|
||||
|
||||
- "n8n-nodes-base.amqpSender" -> "n8n-nodes-base.amqp"
|
||||
- "n8n-nodes-base.bitbucket" -> "n8n-nodes-base.bitbucketTrigger"
|
||||
- "n8n-nodes-base.Coda" -> "n8n-nodes-base.coda"
|
||||
- "n8n-nodes-base.eventbrite" -> "n8n-nodes-base.eventbriteTrigger"
|
||||
- "n8n-nodes-base.Flow" -> "n8n-nodes-base.flow"
|
||||
- "n8n-nodes-base.flow" -> "n8n-nodes-base.flowTrigger"
|
||||
- "n8n-nodes-base.gumroad" -> "n8n-nodes-base.gumroadTrigger"
|
||||
- "n8n-nodes-base.Jira Software Cloud" -> "n8n-nodes-base.jira"
|
||||
- "n8n-nodes-base.Mailchimp" -> "n8n-nodes-base.mailchimpTrigger"
|
||||
- "n8n-nodes-base.PayPal" -> "n8n-nodes-base.payPalTrigger"
|
||||
- "n8n-nodes-base.Read PDF" -> "n8n-nodes-base.readPDF"
|
||||
- "n8n-nodes-base.Rocketchat" -> "n8n-nodes-base.rocketchat"
|
||||
- "n8n-nodes-base.shopify" -> "n8n-nodes-base.shopifyTrigger"
|
||||
- "n8n-nodes-base.shopifyNode" -> "n8n-nodes-base.shopify"
|
||||
- "n8n-nodes-base.stripe" -> "n8n-nodes-base.stripeTrigger"
|
||||
- "n8n-nodes-base.toggl" -> "n8n-nodes-base.togglTrigger"
|
||||
|
||||
Then delete all existing nodes, and then paste the changed JSON directly into n8n. It should then recreate all the nodes and connections again, this time with working nodes.
|
||||
|
||||
|
||||
## 0.62.0
|
||||
|
||||
### What changed?
|
||||
|
@ -496,7 +526,6 @@ If "evaluateExpression(...)" gets used in any Function or FunctionItem Node.
|
|||
|
||||
Simply replace the "evaluateExpression(...)" with "$evaluateExpression(...)".
|
||||
|
||||
|
||||
## 0.52.0
|
||||
|
||||
### What changed?
|
||||
|
@ -517,7 +546,6 @@ Open the "Date & Time"-Nodes and reference the date that should be converted
|
|||
via an expression. Also, set the "Property Name" to the name of the property the
|
||||
converted date should be set on.
|
||||
|
||||
|
||||
## 0.37.0
|
||||
|
||||
### What changed?
|
||||
|
@ -534,7 +562,6 @@ When you currently use the Rocketchat-Node.
|
|||
Open the Rocketchat credentials and fill the parameter `domain`. If you had previously the
|
||||
subdomain "example" set you have to set now "https://example.rocket.chat".
|
||||
|
||||
|
||||
## 0.19.0
|
||||
|
||||
### What changed?
|
||||
|
@ -556,9 +583,7 @@ it and paste it in a text-editor, it will display all the data the node
|
|||
contained). Then set the "Response Format" to "File". Everything will then
|
||||
function again like before.
|
||||
|
||||
|
||||
----------------------------
|
||||
|
||||
---
|
||||
|
||||
### What changed?
|
||||
|
||||
|
@ -576,7 +601,6 @@ When "HTTP Request" nodes get used which have "Response Format" set to "String".
|
|||
After upgrading open all workflows which contain the concerning Nodes and set
|
||||
"Binary Property" to "response".
|
||||
|
||||
|
||||
## 0.18.0
|
||||
|
||||
### What changed?
|
||||
|
@ -591,8 +615,7 @@ When Webhook-Nodes get used which have "Response Mode" set to "Last Node".
|
|||
|
||||
After upgrading open all workflows which contain the concerning Webhook-Nodes and set "Response Mode" again manually to "Last Node".
|
||||
|
||||
|
||||
----------------------------
|
||||
---
|
||||
|
||||
### What changed?
|
||||
|
||||
|
@ -603,6 +626,7 @@ packages with security vulnerabilities we had to switch to a different one.
|
|||
|
||||
When you currently start n8n in your setup directly via its JavaScript file.
|
||||
For example like this:
|
||||
|
||||
```
|
||||
/usr/local/bin/node ./dist/index.js start
|
||||
```
|
||||
|
@ -610,6 +634,7 @@ For example like this:
|
|||
### How to upgrade:
|
||||
|
||||
Change the path to its new location:
|
||||
|
||||
```
|
||||
/usr/local/bin/node bin/n8n start
|
||||
```
|
||||
|
|
|
@ -12,7 +12,7 @@ The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensa
|
|||
|
||||
## Limitations
|
||||
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use.
|
||||
You may distribute the software or provide it to others only if you do so free of charge for non-commercial purposes.
|
||||
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ export class ImportCredentialsCommand extends Command {
|
|||
}
|
||||
|
||||
private reportSuccess(total: number) {
|
||||
console.info(`Successfully imported ${total} ${total === 1 ? 'workflow.' : 'workflows.'}`);
|
||||
console.info(`Successfully imported ${total} ${total === 1 ? 'credential.' : 'credentials.'}`);
|
||||
}
|
||||
|
||||
private async initOwnerCredentialRole() {
|
||||
|
|
|
@ -119,9 +119,14 @@ export class Worker extends Command {
|
|||
|
||||
async runJob(job: Bull.Job, nodeTypes: INodeTypes): Promise<IBullJobResponse> {
|
||||
const jobData = job.data as IBullJobData;
|
||||
const executionDb = (await Db.collections.Execution!.findOne(
|
||||
jobData.executionId,
|
||||
)) as IExecutionFlattedDb;
|
||||
const executionDb = await Db.collections.Execution!.findOne(jobData.executionId);
|
||||
|
||||
if (!executionDb) {
|
||||
LoggerProxy.error('Worker failed to find execution data in database. Cannot continue.', {
|
||||
executionId: jobData.executionId,
|
||||
});
|
||||
throw new Error('Unable to find execution data in database. Aborting execution.');
|
||||
}
|
||||
const currentExecutionDb = ResponseHelper.unflattenExecutionData(executionDb);
|
||||
LoggerProxy.info(
|
||||
`Start job: ${job.id} (Workflow ID: ${currentExecutionDb.workflowData.id} | Execution: ${jobData.executionId})`,
|
||||
|
@ -139,6 +144,13 @@ export class Worker extends Command {
|
|||
findOptions,
|
||||
);
|
||||
if (workflowData === undefined) {
|
||||
LoggerProxy.error(
|
||||
'Worker execution failed because workflow could not be found in database.',
|
||||
{
|
||||
workflowId: currentExecutionDb.workflowData.id,
|
||||
executionId: jobData.executionId,
|
||||
},
|
||||
);
|
||||
throw new Error(
|
||||
`The workflow with the ID "${currentExecutionDb.workflowData.id}" could not be found`,
|
||||
);
|
||||
|
|
|
@ -728,7 +728,7 @@ const config = convict({
|
|||
logs: {
|
||||
level: {
|
||||
doc: 'Log output level',
|
||||
format: ['error', 'warn', 'info', 'verbose', 'debug'],
|
||||
format: ['error', 'warn', 'info', 'verbose', 'debug', 'silent'],
|
||||
default: 'info',
|
||||
env: 'N8N_LOG_LEVEL',
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n",
|
||||
"version": "0.169.0",
|
||||
"version": "0.171.0",
|
||||
"description": "n8n Workflow Automation Tool",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -30,7 +30,7 @@
|
|||
"start:default": "cd bin && ./n8n",
|
||||
"start:windows": "cd bin && n8n",
|
||||
"test": "npm run test:sqlite",
|
||||
"test:sqlite": "export DB_TYPE=sqlite && jest --forceExit",
|
||||
"test:sqlite": "export N8N_LOG_LEVEL='silent'; export DB_TYPE=sqlite; jest",
|
||||
"test:postgres": "export DB_TYPE=postgresdb && jest",
|
||||
"test:mysql": "export DB_TYPE=mysqldb && jest",
|
||||
"watch": "tsc --watch",
|
||||
|
@ -125,10 +125,10 @@
|
|||
"lodash.get": "^4.4.2",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"mysql2": "~2.3.0",
|
||||
"n8n-core": "~0.110.0",
|
||||
"n8n-editor-ui": "~0.136.0",
|
||||
"n8n-nodes-base": "~0.167.0",
|
||||
"n8n-workflow": "~0.92.0",
|
||||
"n8n-core": "~0.112.0",
|
||||
"n8n-editor-ui": "~0.138.0",
|
||||
"n8n-nodes-base": "~0.169.0",
|
||||
"n8n-workflow": "~0.94.0",
|
||||
"nodemailer": "^6.7.1",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
"open": "^7.0.0",
|
||||
|
|
|
@ -682,6 +682,15 @@ export class ActiveWorkflowRunner {
|
|||
(error) => console.error(error),
|
||||
);
|
||||
};
|
||||
returnFunctions.emitError = async (error: Error): Promise<void> => {
|
||||
await this.activeWorkflows?.remove(workflowData.id.toString());
|
||||
this.activationErrors[workflowData.id.toString()] = {
|
||||
time: new Date().getTime(),
|
||||
error: {
|
||||
message: error.message,
|
||||
},
|
||||
};
|
||||
};
|
||||
return returnFunctions;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -11,14 +11,15 @@ class Logger implements ILogger {
|
|||
private logger: winston.Logger;
|
||||
|
||||
constructor() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const level = config.get('logs.level');
|
||||
const level = config.get('logs.level') as string;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
const output = (config.get('logs.output') as string).split(',').map((output) => output.trim());
|
||||
|
||||
this.logger = winston.createLogger({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
level,
|
||||
silent: level === 'silent',
|
||||
});
|
||||
|
||||
if (output.includes('console')) {
|
||||
|
|
|
@ -96,15 +96,13 @@ export function sendSuccessResponse(
|
|||
}
|
||||
}
|
||||
|
||||
export function sendErrorResponse(res: Response, error: ResponseError, shouldLog = true) {
|
||||
export function sendErrorResponse(res: Response, error: ResponseError) {
|
||||
let httpStatusCode = 500;
|
||||
if (error.httpStatusCode) {
|
||||
httpStatusCode = error.httpStatusCode;
|
||||
}
|
||||
|
||||
shouldLog = !process.argv[1].split('/').includes('jest');
|
||||
|
||||
if (process.env.NODE_ENV !== 'production' && shouldLog) {
|
||||
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
|
||||
console.error('ERROR RESPONSE');
|
||||
console.error(error);
|
||||
}
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
import { Workflow } from 'n8n-workflow';
|
||||
import { In, IsNull, Not } from 'typeorm';
|
||||
import express = require('express');
|
||||
import { compare } from 'bcryptjs';
|
||||
|
||||
import { PublicUser } from './Interfaces';
|
||||
import { Db, GenericHelpers, ResponseHelper } from '..';
|
||||
import { Db, ResponseHelper } from '..';
|
||||
import { MAX_PASSWORD_LENGTH, MIN_PASSWORD_LENGTH, User } from '../databases/entities/User';
|
||||
import { Role } from '../databases/entities/Role';
|
||||
import { AuthenticatedRequest } from '../requests';
|
||||
|
@ -216,3 +218,20 @@ export function isPostUsersId(req: express.Request, restEndpoint: string): boole
|
|||
export function isAuthenticatedRequest(request: express.Request): request is AuthenticatedRequest {
|
||||
return request.user !== undefined;
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// hashing
|
||||
// ----------------------------------
|
||||
|
||||
export async function compareHash(str: string, hash: string): Promise<boolean | undefined> {
|
||||
try {
|
||||
return await compare(str, hash);
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes('Invalid salt version')) {
|
||||
error.message +=
|
||||
'. Comparison against unhashed string. Please check that the value compared against has been hashed.';
|
||||
}
|
||||
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,12 @@
|
|||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
import { Request, Response } from 'express';
|
||||
import { compare } from 'bcryptjs';
|
||||
import { IDataObject } from 'n8n-workflow';
|
||||
import { Db, ResponseHelper } from '../..';
|
||||
import { AUTH_COOKIE_NAME } from '../../constants';
|
||||
import { issueCookie, resolveJwt } from '../auth/jwt';
|
||||
import { N8nApp, PublicUser } from '../Interfaces';
|
||||
import { isInstanceOwnerSetup, sanitizeUser } from '../UserManagementHelper';
|
||||
import { compareHash, isInstanceOwnerSetup, sanitizeUser } from '../UserManagementHelper';
|
||||
import { User } from '../../databases/entities/User';
|
||||
import type { LoginRequest } from '../../requests';
|
||||
|
||||
|
@ -43,7 +42,8 @@ export function authenticationMethods(this: N8nApp): void {
|
|||
} catch (error) {
|
||||
throw new Error('Unable to access database.');
|
||||
}
|
||||
if (!user || !user.password || !(await compare(req.body.password, user.password))) {
|
||||
|
||||
if (!user || !user.password || !(await compareHash(req.body.password, user.password))) {
|
||||
// password is empty until user signs up
|
||||
const error = new Error('Wrong username or password. Do you have caps lock on?');
|
||||
// @ts-ignore
|
||||
|
|
|
@ -551,6 +551,7 @@ export async function executeWebhook(
|
|||
if (returnData.data!.main[0]![0] === undefined) {
|
||||
responseCallback(new Error('No item to return got found.'), {});
|
||||
didSendResponse = true;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
data = returnData.data!.main[0]![0].json;
|
||||
|
@ -602,11 +603,13 @@ export async function executeWebhook(
|
|||
if (data === undefined) {
|
||||
responseCallback(new Error('No item to return got found.'), {});
|
||||
didSendResponse = true;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (data.binary === undefined) {
|
||||
responseCallback(new Error('No binary data to return got found.'), {});
|
||||
didSendResponse = true;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const responseBinaryPropertyName = workflow.expression.getSimpleParameterValue(
|
||||
|
|
|
@ -12,6 +12,8 @@ import { randomEmail, randomValidPassword, randomName } from './shared/random';
|
|||
import { getGlobalOwnerRole } from './shared/testDb';
|
||||
import * as testDb from './shared/testDb';
|
||||
|
||||
jest.mock('../../src/telemetry');
|
||||
|
||||
let globalOwnerRole: Role;
|
||||
|
||||
let app: express.Application;
|
||||
|
@ -35,7 +37,7 @@ beforeEach(async () => {
|
|||
email: TEST_USER.email,
|
||||
firstName: TEST_USER.firstName,
|
||||
lastName: TEST_USER.lastName,
|
||||
password: hashSync(TEST_USER.password, genSaltSync(10)),
|
||||
password: TEST_USER.password,
|
||||
globalRole: globalOwnerRole,
|
||||
});
|
||||
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
import express = require('express');
|
||||
|
||||
import * as request from 'supertest';
|
||||
import {
|
||||
REST_PATH_SEGMENT,
|
||||
ROUTES_REQUIRING_AUTHORIZATION,
|
||||
ROUTES_REQUIRING_AUTHENTICATION,
|
||||
} from './shared/constants';
|
||||
|
||||
import * as utils from './shared/utils';
|
||||
import * as testDb from './shared/testDb';
|
||||
|
||||
jest.mock('../../src/telemetry');
|
||||
|
||||
let app: express.Application;
|
||||
let testDbName = '';
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ import { Role } from '../../src/databases/entities/Role';
|
|||
import { User } from '../../src/databases/entities/User';
|
||||
import * as testDb from './shared/testDb';
|
||||
|
||||
jest.mock('../../src/telemetry');
|
||||
|
||||
let app: express.Application;
|
||||
let testDbName = '';
|
||||
let saveCredential: SaveCredentialFunction;
|
||||
|
|
|
@ -10,6 +10,8 @@ import { Role } from '../../src/databases/entities/Role';
|
|||
import { randomValidPassword, randomEmail, randomName, randomString } from './shared/random';
|
||||
import * as testDb from './shared/testDb';
|
||||
|
||||
jest.mock('../../src/telemetry');
|
||||
|
||||
let app: express.Application;
|
||||
let testDbName = '';
|
||||
let globalOwnerRole: Role;
|
||||
|
@ -275,7 +277,7 @@ describe('Member', () => {
|
|||
test('PATCH /me/password should succeed with valid inputs', async () => {
|
||||
const memberPassword = randomValidPassword();
|
||||
const member = await testDb.createUser({
|
||||
password: hashSync(memberPassword, genSaltSync(10)),
|
||||
password: memberPassword,
|
||||
});
|
||||
const authMemberAgent = utils.createAgent(app, { auth: true, user: member });
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ import {
|
|||
randomInvalidPassword,
|
||||
} from './shared/random';
|
||||
|
||||
jest.mock('../../src/telemetry');
|
||||
|
||||
let app: express.Application;
|
||||
let testDbName = '';
|
||||
|
||||
|
@ -82,7 +84,7 @@ test('POST /owner should create owner and enable isInstanceOwnerSetUp', async ()
|
|||
|
||||
test('POST /owner should fail with invalid inputs', async () => {
|
||||
const owner = await Db.collections.User!.findOneOrFail();
|
||||
const authOwnerAgent = await utils.createAgent(app, { auth: true, user: owner });
|
||||
const authOwnerAgent = utils.createAgent(app, { auth: true, user: owner });
|
||||
|
||||
for (const invalidPayload of INVALID_POST_OWNER_PAYLOADS) {
|
||||
const response = await authOwnerAgent.post('/owner').send(invalidPayload);
|
||||
|
@ -92,7 +94,7 @@ test('POST /owner should fail with invalid inputs', async () => {
|
|||
|
||||
test('POST /owner/skip-setup should persist skipping setup to the DB', async () => {
|
||||
const owner = await Db.collections.User!.findOneOrFail();
|
||||
const authOwnerAgent = await utils.createAgent(app, { auth: true, user: owner });
|
||||
const authOwnerAgent = utils.createAgent(app, { auth: true, user: owner });
|
||||
|
||||
const response = await authOwnerAgent.post('/owner/skip-setup').send();
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ import {
|
|||
import { Role } from '../../src/databases/entities/Role';
|
||||
import * as testDb from './shared/testDb';
|
||||
|
||||
jest.mock('../../src/telemetry');
|
||||
|
||||
let app: express.Application;
|
||||
let globalOwnerRole: Role;
|
||||
let testDbName = '';
|
||||
|
|
|
@ -17,7 +17,7 @@ const randomDigit = () => Math.floor(Math.random() * 10);
|
|||
const randomUppercaseLetter = () => chooseRandomly('ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''));
|
||||
|
||||
export const randomValidPassword = () =>
|
||||
randomString(MIN_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH) + randomUppercaseLetter() + randomDigit();
|
||||
randomString(MIN_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH - 2) + randomUppercaseLetter() + randomDigit();
|
||||
|
||||
export const randomInvalidPassword = () =>
|
||||
chooseRandomly([
|
||||
|
|
|
@ -16,6 +16,7 @@ import { sqliteMigrations } from '../../../src/databases/sqlite/migrations';
|
|||
import type { Role } from '../../../src/databases/entities/Role';
|
||||
import type { User } from '../../../src/databases/entities/User';
|
||||
import type { CredentialPayload } from './types';
|
||||
import { genSaltSync, hashSync } from 'bcryptjs';
|
||||
|
||||
/**
|
||||
* Initialize one test DB per suite run, with bootstrap connection if needed.
|
||||
|
@ -188,7 +189,7 @@ export async function createUser(attributes: Partial<User> = {}): Promise<User>
|
|||
const { email, password, firstName, lastName, globalRole, ...rest } = attributes;
|
||||
const user = {
|
||||
email: email ?? randomEmail(),
|
||||
password: password ?? randomValidPassword(),
|
||||
password: hashSync(password ?? randomValidPassword(), genSaltSync(10)),
|
||||
firstName: firstName ?? randomName(),
|
||||
lastName: lastName ?? randomName(),
|
||||
globalRole: globalRole ?? (await getGlobalMemberRole()),
|
||||
|
|
|
@ -89,12 +89,13 @@ export function initTestServer({
|
|||
return testServer.app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-requisite: Mock the telemetry module before calling.
|
||||
*/
|
||||
export function initTestTelemetry() {
|
||||
const mockNodeTypes = { nodeTypes: {} } as INodeTypes;
|
||||
|
||||
void InternalHooksManager.init('test-instance-id', 'test-version', mockNodeTypes);
|
||||
|
||||
jest.spyOn(Telemetry.prototype, 'track').mockResolvedValue();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -117,10 +118,9 @@ const classifyEndpointGroups = (endpointGroups: string[]) => {
|
|||
// ----------------------------------
|
||||
|
||||
/**
|
||||
* Initialize a silent logger for test runs.
|
||||
* Initialize a logger for test runs.
|
||||
*/
|
||||
export function initTestLogger() {
|
||||
config.set('logs.output', 'file'); // declutter console output
|
||||
LoggerProxy.init(getLogger());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import express = require('express');
|
||||
import validator from 'validator';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { compare } from 'bcryptjs';
|
||||
import { compare, genSaltSync, hashSync } from 'bcryptjs';
|
||||
|
||||
import { Db } from '../../src';
|
||||
import config = require('../../config');
|
||||
|
@ -18,6 +18,8 @@ import { WorkflowEntity } from '../../src/databases/entities/WorkflowEntity';
|
|||
import * as utils from './shared/utils';
|
||||
import * as testDb from './shared/testDb';
|
||||
|
||||
jest.mock('../../src/telemetry');
|
||||
|
||||
let app: express.Application;
|
||||
let testDbName = '';
|
||||
let globalOwnerRole: Role;
|
||||
|
@ -404,7 +406,7 @@ test('POST /users/:id should fail with invalid inputs', async () => {
|
|||
}
|
||||
});
|
||||
|
||||
test.skip('POST /users/:id should fail with already accepted invite', async () => {
|
||||
test('POST /users/:id should fail with already accepted invite', async () => {
|
||||
const authlessAgent = utils.createAgent(app);
|
||||
|
||||
const globalMemberRole = await Db.collections.Role!.findOneOrFail({
|
||||
|
@ -414,7 +416,7 @@ test.skip('POST /users/:id should fail with already accepted invite', async () =
|
|||
|
||||
const shell = await Db.collections.User!.save({
|
||||
email: randomEmail(),
|
||||
password: randomValidPassword(), // simulate accepted invite
|
||||
password: hashSync(randomValidPassword(), genSaltSync(10)), // simulate accepted invite
|
||||
globalRole: globalMemberRole,
|
||||
});
|
||||
|
||||
|
@ -424,7 +426,7 @@ test.skip('POST /users/:id should fail with already accepted invite', async () =
|
|||
inviterId: INITIAL_TEST_USER.id,
|
||||
firstName: randomName(),
|
||||
lastName: randomName(),
|
||||
password: newPassword,
|
||||
password: randomValidPassword(),
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(400);
|
||||
|
@ -458,7 +460,7 @@ test('POST /users should fail if user management is disabled', async () => {
|
|||
expect(response.statusCode).toBe(500);
|
||||
});
|
||||
|
||||
test.skip('POST /users should email invites and create user shells', async () => {
|
||||
test('POST /users should email invites and create user shells', async () => {
|
||||
const owner = await Db.collections.User!.findOneOrFail();
|
||||
const authOwnerAgent = utils.createAgent(app, { auth: true, user: owner });
|
||||
|
||||
|
@ -502,7 +504,7 @@ test.skip('POST /users should email invites and create user shells', async () =>
|
|||
}
|
||||
});
|
||||
|
||||
test.skip('POST /users should fail with invalid inputs', async () => {
|
||||
test('POST /users should fail with invalid inputs', async () => {
|
||||
const owner = await Db.collections.User!.findOneOrFail();
|
||||
const authOwnerAgent = utils.createAgent(app, { auth: true, user: owner });
|
||||
|
||||
|
@ -525,7 +527,7 @@ test.skip('POST /users should fail with invalid inputs', async () => {
|
|||
}
|
||||
});
|
||||
|
||||
test.skip('POST /users should ignore an empty payload', async () => {
|
||||
test('POST /users should ignore an empty payload', async () => {
|
||||
const owner = await Db.collections.User!.findOneOrFail();
|
||||
const authOwnerAgent = utils.createAgent(app, { auth: true, user: owner });
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensa
|
|||
|
||||
## Limitations
|
||||
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use.
|
||||
You may distribute the software or provide it to others only if you do so free of charge for non-commercial purposes.
|
||||
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-core",
|
||||
"version": "0.110.0",
|
||||
"version": "0.112.0",
|
||||
"description": "Core functionality of n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -52,7 +52,7 @@
|
|||
"form-data": "^4.0.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"mime-types": "^2.1.27",
|
||||
"n8n-workflow": "~0.92.0",
|
||||
"n8n-workflow": "~0.94.0",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
"p-cancelable": "^2.0.0",
|
||||
"qs": "^6.10.1",
|
||||
|
|
|
@ -1750,6 +1750,9 @@ export function getExecuteTriggerFunctions(
|
|||
emit: (data: INodeExecutionData[][]): void => {
|
||||
throw new Error('Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!');
|
||||
},
|
||||
emitError: (error: Error): void => {
|
||||
throw new Error('Overwrite NodeExecuteFunctions.getExecuteTriggerFunctions.emit function!');
|
||||
},
|
||||
async getCredentials(type: string): Promise<ICredentialDataDecryptedObject | undefined> {
|
||||
return getCredentials(workflow, node, type, additionalData, mode);
|
||||
},
|
||||
|
|
|
@ -12,7 +12,7 @@ The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensa
|
|||
|
||||
## Limitations
|
||||
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use.
|
||||
You may distribute the software or provide it to others only if you do so free of charge for non-commercial purposes.
|
||||
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-design-system",
|
||||
"version": "0.15.0",
|
||||
"version": "0.16.0",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
"author": {
|
||||
|
|
|
@ -12,7 +12,7 @@ The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensa
|
|||
|
||||
## Limitations
|
||||
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use.
|
||||
You may distribute the software or provide it to others only if you do so free of charge for non-commercial purposes.
|
||||
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-editor-ui",
|
||||
"version": "0.136.0",
|
||||
"version": "0.138.0",
|
||||
"description": "Workflow Editor UI for n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -26,7 +26,7 @@
|
|||
"dependencies": {
|
||||
"@fontsource/open-sans": "^4.5.0",
|
||||
"luxon": "^2.3.0",
|
||||
"n8n-design-system": "~0.15.0",
|
||||
"n8n-design-system": "~0.16.0",
|
||||
"monaco-editor": "^0.29.1",
|
||||
"timeago.js": "^4.0.2",
|
||||
"v-click-outside": "^3.1.2",
|
||||
|
@ -77,7 +77,7 @@
|
|||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.set": "^4.3.2",
|
||||
"n8n-workflow": "~0.92.0",
|
||||
"n8n-workflow": "~0.94.0",
|
||||
"monaco-editor-webpack-plugin": "^5.0.0",
|
||||
"normalize-wheel": "^1.0.1",
|
||||
"prismjs": "^1.17.1",
|
||||
|
|
|
@ -170,6 +170,7 @@
|
|||
|
||||
<div :class="$style.binaryButtonContainer">
|
||||
<n8n-button size="small" :label="$locale.baseText('runData.showBinaryData')" class="binary-data-show-data-button" @click="displayBinaryData(index, key)" />
|
||||
<n8n-button v-if="isDownloadable(index, key)" size="small" type="outline" :label="$locale.baseText('runData.downloadBinaryData')" class="binary-data-show-data-button" @click="downloadBinaryData(index, key)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -197,6 +198,7 @@
|
|||
import VueJsonPretty from 'vue-json-pretty';
|
||||
import {
|
||||
GenericValue,
|
||||
IBinaryData,
|
||||
IBinaryKeyData,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
|
@ -231,6 +233,8 @@ import { nodeHelpers } from '@/components/mixins/nodeHelpers';
|
|||
import mixins from 'vue-typed-mixins';
|
||||
import Vue from 'vue/types/umd';
|
||||
|
||||
import { saveAs } from 'file-saver';
|
||||
|
||||
// A path that does not exist so that nothing is selected by default
|
||||
const deselectedPlaceholder = '_!^&*';
|
||||
|
||||
|
@ -568,6 +572,24 @@ export default mixins(
|
|||
dataItemClicked (path: string, data: object | number | string) {
|
||||
this.state.value = data;
|
||||
},
|
||||
isDownloadable (index: number, key: string): boolean {
|
||||
const binaryDataItem: IBinaryData = this.binaryData[index][key];
|
||||
return !!(binaryDataItem.mimeType && binaryDataItem.fileName);
|
||||
},
|
||||
async downloadBinaryData (index: number, key: string) {
|
||||
const binaryDataItem: IBinaryData = this.binaryData[index][key];
|
||||
|
||||
let bufferString = 'data:' + binaryDataItem.mimeType + ';base64,';
|
||||
if(binaryDataItem.id) {
|
||||
bufferString += await this.restApi().getBinaryBufferString(binaryDataItem.id);
|
||||
} else {
|
||||
bufferString += binaryDataItem.data;
|
||||
}
|
||||
|
||||
const data = await fetch(bufferString);
|
||||
const blob = await data.blob();
|
||||
saveAs(blob, binaryDataItem.fileName);
|
||||
},
|
||||
displayBinaryData (index: number, key: string) {
|
||||
this.binaryDataDisplayVisible = true;
|
||||
|
||||
|
|
|
@ -742,7 +742,12 @@
|
|||
"mimeType": "Mime Type",
|
||||
"ms": "ms",
|
||||
"noBinaryDataFound": "No binary data found",
|
||||
"showBinaryData": "Show Binary Data",
|
||||
"noData": "No data",
|
||||
"noTextDataFound": "No text data found",
|
||||
"nodeReturnedALargeAmountOfData": "Node returned a large amount of data",
|
||||
"output": "Output",
|
||||
"downloadBinaryData": "Download",
|
||||
"showBinaryData": "View",
|
||||
"startTime": "Start Time",
|
||||
"table": "Table"
|
||||
},
|
||||
|
|
|
@ -12,7 +12,7 @@ The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensa
|
|||
|
||||
## Limitations
|
||||
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use.
|
||||
You may distribute the software or provide it to others only if you do so free of charge for non-commercial purposes.
|
||||
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-node-dev",
|
||||
"version": "0.49.0",
|
||||
"version": "0.51.0",
|
||||
"description": "CLI to simplify n8n credentials/node development",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -61,8 +61,8 @@
|
|||
"change-case": "^4.1.1",
|
||||
"copyfiles": "^2.1.1",
|
||||
"inquirer": "^7.0.1",
|
||||
"n8n-core": "~0.110.0",
|
||||
"n8n-workflow": "~0.92.0",
|
||||
"n8n-core": "~0.112.0",
|
||||
"n8n-workflow": "~0.94.0",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
"replace-in-file": "^6.0.0",
|
||||
"request": "^2.88.2",
|
||||
|
|
|
@ -12,7 +12,7 @@ The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensa
|
|||
|
||||
## Limitations
|
||||
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use.
|
||||
You may distribute the software or provide it to others only if you do so free of charge for non-commercial purposes.
|
||||
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
||||
|
||||
|
|
|
@ -22,14 +22,14 @@ export class MauticOAuth2Api implements ICredentialType {
|
|||
displayName: 'Authorization URL',
|
||||
name: 'authUrl',
|
||||
type: 'hidden',
|
||||
default: '={{$self["url"]}}/oauth/v2/authorize',
|
||||
default: '={{$self["url"].endsWith("/") ? $self["url"].slice(0, -1) : $self["url"]}}/oauth/v2/authorize',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Access Token URL',
|
||||
name: 'accessTokenUrl',
|
||||
type: 'hidden',
|
||||
default: '={{$self["url"]}}/oauth/v2/token',
|
||||
default: '={{$self["url"].endsWith("/") ? $self["url"].slice(0, -1) : $self["url"]}}/oauth/v2/token',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -16,7 +16,7 @@ export class MicrosoftTeamsOAuth2Api implements ICredentialType {
|
|||
displayName: 'Scope',
|
||||
name: 'scope',
|
||||
type: 'hidden',
|
||||
default: 'openid offline_access User.ReadWrite.All Group.ReadWrite.All',
|
||||
default: 'openid offline_access User.ReadWrite.All Group.ReadWrite.All Chat.ReadWrite',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import {
|
||||
IAuthenticateBasicAuth,
|
||||
ICredentialTestRequest,
|
||||
ICredentialType,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class ServiceNowBasicApi implements ICredentialType {
|
||||
name = 'serviceNowBasicApi';
|
||||
extends = [
|
||||
'httpBasicAuth',
|
||||
];
|
||||
displayName = 'ServiceNow Basic Auth API';
|
||||
documentationUrl = 'serviceNow';
|
||||
properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Subdomain',
|
||||
name: 'subdomain',
|
||||
type: 'string',
|
||||
default: '',
|
||||
hint: 'The subdomain can be extracted from the URL. If the URL is: https://dev99890.service-now.com the subdomain is dev99890',
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
authenticate: IAuthenticateBasicAuth = {
|
||||
type: 'basicAuth',
|
||||
properties: {},
|
||||
};
|
||||
test: ICredentialTestRequest = {
|
||||
request: {
|
||||
baseURL: '=https://{{$credentials?.subdomain}}.service-now.com',
|
||||
url: '/api/now/table/sys_user_role',
|
||||
},
|
||||
};
|
||||
}
|
|
@ -16,8 +16,7 @@ export class ServiceNowOAuth2Api implements ICredentialType {
|
|||
name: 'subdomain',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: 'n8n',
|
||||
description: 'The subdomain of your ServiceNow environment',
|
||||
hint: 'The subdomain can be extracted from the URL. If the URL is: https://dev99890.service-now.com the subdomain is dev99890',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
JsonObject,
|
||||
NodeApiError,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
@ -191,7 +192,7 @@ export class AwsLambda implements INodeType {
|
|||
},
|
||||
);
|
||||
|
||||
if (responseData !== null && responseData.errorMessage !== undefined) {
|
||||
if (responseData !== null && responseData?.errorMessage !== undefined) {
|
||||
let errorMessage = responseData.errorMessage;
|
||||
|
||||
if (responseData.stackTrace) {
|
||||
|
@ -206,7 +207,7 @@ export class AwsLambda implements INodeType {
|
|||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.message });
|
||||
returnData.push({ error: (error as JsonObject).message });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
import { set } from 'lodash';
|
||||
import { IExecuteFunctions } from 'n8n-core';
|
||||
import {
|
||||
set,
|
||||
} from 'lodash';
|
||||
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ILoadOptionsFunctions,
|
||||
INodeExecutionData,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
JsonObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
|
@ -14,8 +21,11 @@ import {
|
|||
createHmac,
|
||||
createSign,
|
||||
getHashes,
|
||||
randomBytes,
|
||||
} from 'crypto';
|
||||
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export class Crypto implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Crypto',
|
||||
|
@ -37,19 +47,24 @@ export class Crypto implements INodeType {
|
|||
name: 'action',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Generate',
|
||||
description: 'Generate random string',
|
||||
value: 'generate',
|
||||
},
|
||||
{
|
||||
name: 'Hash',
|
||||
description: 'Hash a text in a specified format.',
|
||||
description: 'Hash a text in a specified format',
|
||||
value: 'hash',
|
||||
},
|
||||
{
|
||||
name: 'Hmac',
|
||||
description: 'Hmac a text in a specified format.',
|
||||
description: 'Hmac a text in a specified format',
|
||||
value: 'hmac',
|
||||
},
|
||||
{
|
||||
name: 'Sign',
|
||||
description: 'Sign a string using a private key.',
|
||||
description: 'Sign a string using a private key',
|
||||
value: 'sign',
|
||||
},
|
||||
],
|
||||
|
@ -100,7 +115,7 @@ export class Crypto implements INodeType {
|
|||
},
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The value that should be hashed.',
|
||||
description: 'The value that should be hashed',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
|
@ -116,7 +131,7 @@ export class Crypto implements INodeType {
|
|||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the property to which to write the hash.',
|
||||
description: 'Name of the property to which to write the hash',
|
||||
},
|
||||
{
|
||||
displayName: 'Encoding',
|
||||
|
@ -187,7 +202,7 @@ export class Crypto implements INodeType {
|
|||
},
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The value of which the hmac should be created.',
|
||||
description: 'The value of which the hmac should be created',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
|
@ -203,7 +218,7 @@ export class Crypto implements INodeType {
|
|||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the property to which to write the hmac.',
|
||||
description: 'Name of the property to which to write the hmac',
|
||||
},
|
||||
{
|
||||
displayName: 'Secret',
|
||||
|
@ -255,7 +270,7 @@ export class Crypto implements INodeType {
|
|||
},
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The value that should be signed.',
|
||||
description: 'The value that should be signed',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
|
@ -271,7 +286,7 @@ export class Crypto implements INodeType {
|
|||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the property to which to write the signed value.',
|
||||
description: 'Name of the property to which to write the signed value',
|
||||
},
|
||||
{
|
||||
displayName: 'Algorithm',
|
||||
|
@ -328,10 +343,77 @@ export class Crypto implements INodeType {
|
|||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
description: 'Private key to use when signing the string.',
|
||||
description: 'Private key to use when signing the string',
|
||||
default: '',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Property Name',
|
||||
name: 'dataPropertyName',
|
||||
type: 'string',
|
||||
default: 'data',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'generate',
|
||||
],
|
||||
},
|
||||
},
|
||||
description: 'Name of the property to which to write the random string',
|
||||
},
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'encodingType',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'generate',
|
||||
],
|
||||
},
|
||||
},
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'ASCII',
|
||||
value: 'ascii',
|
||||
},
|
||||
{
|
||||
name: 'BASE64',
|
||||
value: 'base64',
|
||||
},
|
||||
{
|
||||
name: 'HEX',
|
||||
value: 'hex',
|
||||
},
|
||||
{
|
||||
name: 'UUID',
|
||||
value: 'uuid',
|
||||
},
|
||||
],
|
||||
default: 'uuid',
|
||||
description: 'Encoding that will be used to generate string',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Length',
|
||||
name: 'stringLength',
|
||||
type: 'number',
|
||||
default: 32,
|
||||
description: 'Length of the generated string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
action: [
|
||||
'generate',
|
||||
],
|
||||
encodingType: [
|
||||
'ascii',
|
||||
'base64',
|
||||
'hex',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -369,9 +451,22 @@ export class Crypto implements INodeType {
|
|||
|
||||
item = items[i];
|
||||
const dataPropertyName = this.getNodeParameter('dataPropertyName', i) as string;
|
||||
const value = this.getNodeParameter('value', i) as string;
|
||||
const value = this.getNodeParameter('value', i, '') as string;
|
||||
let newValue;
|
||||
|
||||
if (action === 'generate') {
|
||||
const encodingType = this.getNodeParameter('encodingType', i) as string;
|
||||
if (encodingType === 'uuid') {
|
||||
newValue = uuid();
|
||||
} else {
|
||||
const stringLength = this.getNodeParameter('stringLength', i) as number;
|
||||
if (encodingType === 'base64') {
|
||||
newValue = randomBytes(stringLength).toString(encodingType as BufferEncoding).replace(/\W/g, '').slice(0, stringLength);
|
||||
} else {
|
||||
newValue = randomBytes(stringLength).toString(encodingType as BufferEncoding).slice(0, stringLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (action === 'hash') {
|
||||
const type = this.getNodeParameter('type', i) as string;
|
||||
const encoding = this.getNodeParameter('encoding', i) as BinaryToTextEncoding;
|
||||
|
@ -413,10 +508,10 @@ export class Crypto implements INodeType {
|
|||
set(newItem, `json.${dataPropertyName}`, newValue);
|
||||
|
||||
returnData.push(newItem);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({json:{ error: error.message }});
|
||||
returnData.push({ json: { error: (error as JsonObject).message } });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import { ITriggerFunctions } from 'n8n-core';
|
||||
import {
|
||||
createDeferredPromise,
|
||||
IBinaryData,
|
||||
IBinaryKeyData,
|
||||
IDataObject,
|
||||
IDeferredPromise,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
ITriggerResponse,
|
||||
LoggerProxy,
|
||||
LoggerProxy as Logger,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
@ -25,10 +27,6 @@ import {
|
|||
|
||||
import * as lodash from 'lodash';
|
||||
|
||||
import {
|
||||
LoggerProxy as Logger
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export class EmailReadImap implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'EmailReadImap',
|
||||
|
@ -377,6 +375,8 @@ export class EmailReadImap implements INodeType {
|
|||
return newEmails;
|
||||
};
|
||||
|
||||
const returnedPromise: IDeferredPromise<void> | undefined = await createDeferredPromise<void>();
|
||||
|
||||
const establishConnection = (): Promise<ImapSimple> => {
|
||||
|
||||
let searchCriteria = [
|
||||
|
@ -425,7 +425,11 @@ export class EmailReadImap implements INodeType {
|
|||
}
|
||||
} catch (error) {
|
||||
Logger.error('Email Read Imap node encountered an error fetching new emails', { error });
|
||||
throw error;
|
||||
// Wait with resolving till the returnedPromise got resolved, else n8n will be unhappy
|
||||
// if it receives an error before the workflow got activated
|
||||
returnedPromise.promise().then(() => {
|
||||
this.emitError(error as Error);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -475,10 +479,12 @@ export class EmailReadImap implements INodeType {
|
|||
await connection.end();
|
||||
}
|
||||
|
||||
// Resolve returned-promise so that waiting errors can be emitted
|
||||
returnedPromise.resolve();
|
||||
|
||||
return {
|
||||
closeFunction,
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ export const campaignOperations: INodeProperties[] = [
|
|||
type: 'options',
|
||||
default: 'get',
|
||||
description: 'Operation to perform',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Add Contact',
|
||||
|
@ -18,6 +19,10 @@ export const campaignOperations: INodeProperties[] = [
|
|||
name: 'Create',
|
||||
value: 'create',
|
||||
},
|
||||
{
|
||||
name: 'Duplicate',
|
||||
value: 'duplicate',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
|
@ -58,7 +63,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
},
|
||||
default: [],
|
||||
required: true,
|
||||
description: 'The ID of the campaign to add the contact to.',
|
||||
description: 'The ID of the campaign to add the contact to',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -76,7 +81,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'The email of the contact to add to the campaign.',
|
||||
description: 'The email of the contact to add to the campaign',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -113,7 +118,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
description: 'Filter by custom fields ',
|
||||
description: 'Filter by custom fields',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
|
@ -125,14 +130,14 @@ export const campaignFields: INodeProperties[] = [
|
|||
name: 'fieldName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The name of the field to add custom field to.',
|
||||
description: 'The name of the field to add custom field to',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The value to set on custom field.',
|
||||
description: 'The value to set on custom field',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -143,49 +148,49 @@ export const campaignFields: INodeProperties[] = [
|
|||
name: 'firstName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'First name of the contact to add.',
|
||||
description: 'First name of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Contacted',
|
||||
name: 'lastContacted',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Last contacted date of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'lastName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Last name of the contact to add.',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Contacted',
|
||||
name: 'lastContacted',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Last contacted date of the contact to add.',
|
||||
description: 'Last name of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Open',
|
||||
name: 'lastOpen',
|
||||
type: 'string',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Last opened date of the contact to add.',
|
||||
description: 'Last opened date of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Replied',
|
||||
name: 'lastReplied',
|
||||
type: 'string',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Last replied date of the contact to add.',
|
||||
description: 'Last replied date of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Mails Sent',
|
||||
name: 'mailsSent',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
description: 'Number of emails sent to the contact to add.',
|
||||
description: 'Number of emails sent to the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone Number',
|
||||
name: 'phoneNumber',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Phone number of the contact to add.',
|
||||
description: 'Phone number of the contact to add',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -199,7 +204,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'The name of the campaign to create.',
|
||||
description: 'The name of the campaign to create',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -221,7 +226,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'The ID of the campaign to retrieve.',
|
||||
description: 'The ID of the campaign to retrieve',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -242,7 +247,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -259,7 +264,7 @@ export const campaignFields: INodeProperties[] = [
|
|||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 100,
|
||||
description: 'The number of results to return.',
|
||||
description: 'Max number of results to return',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
|
@ -323,4 +328,93 @@ export const campaignFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// campaign: duplicate
|
||||
// ----------------------------------
|
||||
{
|
||||
displayName: 'Campaign ID',
|
||||
name: 'campaignId',
|
||||
type: 'options',
|
||||
default: '',
|
||||
required: true,
|
||||
description: 'The ID of the campaign to duplicate',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getCampaigns',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
operation: [
|
||||
'duplicate',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'New Campaign Name',
|
||||
name: 'campaignName',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'The name of the new campaign to create',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
operation: [
|
||||
'duplicate',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'duplicate',
|
||||
],
|
||||
resource: [
|
||||
'campaign',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Copy Contacts',
|
||||
name: 'copyContacts',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to copy all the contacts from the original campaign',
|
||||
},
|
||||
{
|
||||
displayName: 'Copy Email Provider',
|
||||
name: 'copyProvider',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to set the same email provider than the original campaign',
|
||||
},
|
||||
{
|
||||
displayName: 'Copy Email Sequence',
|
||||
name: 'copyMails',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to copy all the steps of the email sequence from the original campaign',
|
||||
},
|
||||
{
|
||||
displayName: 'Copy Global Settings',
|
||||
name: 'copySettings',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Whether to copy all the general settings from the original campaign',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
|
@ -7,8 +7,9 @@ export const contactListOperations: INodeProperties[] = [
|
|||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
default: 'get',
|
||||
default: 'getAll',
|
||||
description: 'Operation to perform',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Add',
|
||||
|
@ -42,7 +43,7 @@ export const contactListFields: INodeProperties[] = [
|
|||
},
|
||||
default: [],
|
||||
required: true,
|
||||
description: 'The ID of the contact list to add the contact to.',
|
||||
description: 'The ID of the contact list to add the contact to',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -60,7 +61,7 @@ export const contactListFields: INodeProperties[] = [
|
|||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'The email of the contact to add to the contact list.',
|
||||
description: 'The email of the contact to add to the contact list',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -97,7 +98,7 @@ export const contactListFields: INodeProperties[] = [
|
|||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
description: 'Filter by custom fields ',
|
||||
description: 'Filter by custom fields',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
|
@ -109,14 +110,14 @@ export const contactListFields: INodeProperties[] = [
|
|||
name: 'fieldName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The name of the field to add custom field to.',
|
||||
description: 'The name of the field to add custom field to',
|
||||
},
|
||||
{
|
||||
displayName: 'Value',
|
||||
name: 'value',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The value to set on custom field.',
|
||||
description: 'The value to set on custom field',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -127,49 +128,49 @@ export const contactListFields: INodeProperties[] = [
|
|||
name: 'firstName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'First name of the contact to add.',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'lastName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Last name of the contact to add.',
|
||||
description: 'First name of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Contacted',
|
||||
name: 'lastContacted',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Last contacted date of the contact to add.',
|
||||
description: 'Last contacted date of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Name',
|
||||
name: 'lastName',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Last name of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Open',
|
||||
name: 'lastOpen',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Last opened date of the contact to add.',
|
||||
description: 'Last opened date of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Last Replied',
|
||||
name: 'lastReplied',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: 'Last replied date of the contact to add.',
|
||||
description: 'Last replied date of the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Mails Sent',
|
||||
name: 'mailsSent',
|
||||
type: 'number',
|
||||
default: 0,
|
||||
description: 'Number of emails sent to the contact to add.',
|
||||
description: 'Number of emails sent to the contact to add',
|
||||
},
|
||||
{
|
||||
displayName: 'Phone Number',
|
||||
name: 'phoneNumber',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Phone number of the contact to add.',
|
||||
description: 'Phone number of the contact to add',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -182,7 +183,7 @@ export const contactListFields: INodeProperties[] = [
|
|||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -199,7 +200,7 @@ export const contactListFields: INodeProperties[] = [
|
|||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 100,
|
||||
description: 'The number of results to return.',
|
||||
description: 'Max number of results to return',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
|
|
|
@ -7,10 +7,12 @@ import {
|
|||
ILoadOptionsFunctions,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription
|
||||
INodeTypeDescription,
|
||||
JsonObject
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
emeliaApiTest,
|
||||
emeliaGraphqlRequest,
|
||||
loadResource,
|
||||
} from './GenericFunctions';
|
||||
|
@ -36,7 +38,7 @@ export class Emelia implements INodeType {
|
|||
icon: 'file:emelia.svg',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["resource"] + ": " + $parameter["operation"]}}',
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Consume the Emelia API',
|
||||
defaults: {
|
||||
name: 'Emelia',
|
||||
|
@ -47,6 +49,7 @@ export class Emelia implements INodeType {
|
|||
{
|
||||
name: 'emeliaApi',
|
||||
required: true,
|
||||
testedBy: 'emeliaApiTest',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
|
@ -54,6 +57,7 @@ export class Emelia implements INodeType {
|
|||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Campaign',
|
||||
|
@ -66,7 +70,7 @@ export class Emelia implements INodeType {
|
|||
],
|
||||
default: 'campaign',
|
||||
required: true,
|
||||
description: 'The resource to operate on.',
|
||||
description: 'The resource to operate on',
|
||||
},
|
||||
...campaignOperations,
|
||||
...campaignFields,
|
||||
|
@ -76,6 +80,10 @@ export class Emelia implements INodeType {
|
|||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
emeliaApiTest,
|
||||
},
|
||||
|
||||
loadOptions: {
|
||||
async getCampaigns(this: ILoadOptionsFunctions) {
|
||||
return loadResource.call(this, 'campaign');
|
||||
|
@ -290,6 +298,46 @@ export class Emelia implements INodeType {
|
|||
|
||||
returnData.push({ success: true });
|
||||
|
||||
} else if (operation === 'duplicate') {
|
||||
|
||||
// ----------------------------------
|
||||
// campaign: duplicate
|
||||
// ----------------------------------
|
||||
|
||||
const options = this.getNodeParameter('options', i) as IDataObject;
|
||||
const variables = {
|
||||
fromId: this.getNodeParameter('campaignId', i),
|
||||
name: this.getNodeParameter('campaignName', i),
|
||||
copySettings: true,
|
||||
copyMails: true,
|
||||
copyContacts: false,
|
||||
copyProvider: true,
|
||||
...options,
|
||||
};
|
||||
const { data: { duplicateCampaign } } = await emeliaGraphqlRequest.call(this, {
|
||||
query: `
|
||||
mutation duplicateCampaign(
|
||||
$fromId: ID!
|
||||
$name: String!
|
||||
$copySettings: Boolean!
|
||||
$copyMails: Boolean!
|
||||
$copyContacts: Boolean!
|
||||
$copyProvider: Boolean!
|
||||
) {
|
||||
duplicateCampaign(
|
||||
fromId: $fromId
|
||||
name: $name
|
||||
copySettings: $copySettings
|
||||
copyMails: $copyMails
|
||||
copyContacts: $copyContacts
|
||||
copyProvider: $copyProvider
|
||||
)
|
||||
}`,
|
||||
operationName: 'duplicateCampaign',
|
||||
variables,
|
||||
});
|
||||
|
||||
returnData.push({ _id: duplicateCampaign });
|
||||
}
|
||||
|
||||
} else if (resource === 'contactList') {
|
||||
|
@ -373,7 +421,7 @@ export class Emelia implements INodeType {
|
|||
} catch (error) {
|
||||
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.message });
|
||||
returnData.push({ error: (error as JsonObject).message });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
|
||||
import {
|
||||
emeliaApiRequest,
|
||||
emeliaApiTest,
|
||||
emeliaGraphqlRequest,
|
||||
} from './GenericFunctions';
|
||||
|
||||
|
@ -23,6 +24,7 @@ export class EmeliaTrigger implements INodeType {
|
|||
displayName: 'Emelia Trigger',
|
||||
name: 'emeliaTrigger',
|
||||
icon: 'file:emelia.svg',
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
group: ['trigger'],
|
||||
version: 1,
|
||||
description: 'Handle Emelia campaign activity events via webhooks',
|
||||
|
@ -35,6 +37,7 @@ export class EmeliaTrigger implements INodeType {
|
|||
{
|
||||
name: 'emeliaApi',
|
||||
required: true,
|
||||
testedBy: 'emeliaApiTest',
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
|
@ -93,6 +96,10 @@ export class EmeliaTrigger implements INodeType {
|
|||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
emeliaApiTest,
|
||||
},
|
||||
|
||||
loadOptions: {
|
||||
async getCampaigns(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const responseData = await emeliaGraphqlRequest.call(this, {
|
||||
|
|
|
@ -4,8 +4,12 @@ import {
|
|||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IHookFunctions,
|
||||
INodeCredentialTestResult,
|
||||
INodePropertyOptions,
|
||||
JsonObject,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
@ -51,7 +55,7 @@ export async function emeliaApiRequest(
|
|||
try {
|
||||
return await this.helpers.request!.call(this, options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
throw new NodeApiError(this.getNode(), (error as JsonObject));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,5 +95,47 @@ export async function loadResource(
|
|||
name: campaign.name,
|
||||
value: campaign._id,
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
export async function emeliaApiTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<INodeCredentialTestResult> {
|
||||
const credentials = credential.data;
|
||||
|
||||
const body = {
|
||||
query: `
|
||||
query all_campaigns {
|
||||
all_campaigns {
|
||||
_id
|
||||
name
|
||||
status
|
||||
createdAt
|
||||
stats {
|
||||
mailsSent
|
||||
}
|
||||
}
|
||||
}`,
|
||||
operationName: 'all_campaigns',
|
||||
};
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
Authorization: credentials?.apiKey,
|
||||
},
|
||||
method: 'POST',
|
||||
body,
|
||||
uri: `https://graphql.emelia.io/graphql`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
try {
|
||||
await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: `Connection details not valid: ${(error as JsonObject).message}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Authentication successful!',
|
||||
};
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ export class Ftp implements INodeType {
|
|||
outputs: ['main'],
|
||||
credentials: [
|
||||
{
|
||||
// nodelinter-ignore-next-line
|
||||
name: 'ftp',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
|
@ -62,6 +63,7 @@ export class Ftp implements INodeType {
|
|||
},
|
||||
},
|
||||
{
|
||||
// nodelinter-ignore-next-line
|
||||
name: 'sftp',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
|
@ -124,6 +126,7 @@ export class Ftp implements INodeType {
|
|||
],
|
||||
default: 'download',
|
||||
description: 'Operation to perform.',
|
||||
noDataExpression: true,
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
|
@ -253,6 +256,29 @@ export class Ftp implements INodeType {
|
|||
description: 'The new path',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'rename',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Create Directories',
|
||||
name: 'createDirectories',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: `Recursively create destination directory when renaming an existing file or folder`,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// ----------------------------------
|
||||
// upload
|
||||
|
@ -381,8 +407,8 @@ export class Ftp implements INodeType {
|
|||
throw new NodeOperationError(this.getNode(), 'Failed to get credentials!');
|
||||
}
|
||||
|
||||
let ftp : ftpClient;
|
||||
let sftp : sftpClient;
|
||||
let ftp: ftpClient;
|
||||
let sftp: sftpClient;
|
||||
|
||||
if (protocol === 'sftp') {
|
||||
sftp = new sftpClient();
|
||||
|
@ -452,9 +478,13 @@ export class Ftp implements INodeType {
|
|||
|
||||
if (operation === 'rename') {
|
||||
const oldPath = this.getNodeParameter('oldPath', i) as string;
|
||||
|
||||
const { createDirectories = false } = this.getNodeParameter('options', i) as { createDirectories: boolean };
|
||||
const newPath = this.getNodeParameter('newPath', i) as string;
|
||||
|
||||
if (createDirectories) {
|
||||
await recursivelyCreateSftpDirs(sftp!, newPath);
|
||||
}
|
||||
|
||||
responseData = await sftp!.rename(oldPath, newPath);
|
||||
|
||||
returnItems.push({ json: { success: true } });
|
||||
|
@ -475,16 +505,7 @@ export class Ftp implements INodeType {
|
|||
|
||||
if (operation === 'upload') {
|
||||
const remotePath = this.getNodeParameter('path', i) as string;
|
||||
|
||||
// Check if dir path exists
|
||||
const dirPath = dirname(remotePath);
|
||||
const dirExists = await sftp!.exists(dirPath);
|
||||
|
||||
// If dir does not exist, create all recursively in path
|
||||
if (!dirExists) {
|
||||
// Create directory
|
||||
await sftp!.mkdir(dirPath, true);
|
||||
}
|
||||
await recursivelyCreateSftpDirs(sftp!, remotePath);
|
||||
|
||||
if (this.getNodeParameter('binaryData', i) === true) {
|
||||
// Is binary file to upload
|
||||
|
@ -635,7 +656,7 @@ export class Ftp implements INodeType {
|
|||
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
return this.prepareOutputData([{json:{ error: error.message }}]);
|
||||
return this.prepareOutputData([{ json: { error: error.message } }]);
|
||||
}
|
||||
|
||||
throw error;
|
||||
|
@ -661,17 +682,17 @@ function normalizeSFtpItem(input: sftpClient.FileInfo, path: string, recursive =
|
|||
}
|
||||
|
||||
async function callRecursiveList(path: string, client: sftpClient | ftpClient, normalizeFunction: (input: ftpClient.ListingElement & sftpClient.FileInfo, path: string, recursive?: boolean) => void) {
|
||||
const pathArray : string[] = [path];
|
||||
const pathArray: string[] = [path];
|
||||
let currentPath = path;
|
||||
const directoryItems : sftpClient.FileInfo[] = [];
|
||||
const directoryItems: sftpClient.FileInfo[] = [];
|
||||
let index = 0;
|
||||
|
||||
do {
|
||||
// tslint:disable-next-line: array-type
|
||||
const returnData : sftpClient.FileInfo[] | (string | ftpClient.ListingElement)[] = await client.list(pathArray[index]);
|
||||
const returnData: sftpClient.FileInfo[] | (string | ftpClient.ListingElement)[] = await client.list(pathArray[index]);
|
||||
|
||||
// @ts-ignore
|
||||
returnData.map((item : sftpClient.FileInfo) => {
|
||||
returnData.map((item: sftpClient.FileInfo) => {
|
||||
if ((pathArray[index] as string).endsWith('/')) {
|
||||
currentPath = `${pathArray[index]}${item.name}`;
|
||||
} else {
|
||||
|
@ -693,3 +714,12 @@ async function callRecursiveList(path: string, client: sftpClient | ftpClient, n
|
|||
|
||||
return directoryItems;
|
||||
}
|
||||
|
||||
async function recursivelyCreateSftpDirs(sftp: sftpClient, path: string) {
|
||||
const dirPath = dirname(path);
|
||||
const dirExists = await sftp!.exists(dirPath);
|
||||
|
||||
if (!dirExists) {
|
||||
await sftp!.mkdir(dirPath, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -171,7 +171,7 @@ export class GoogleTasks implements INodeType {
|
|||
//https://developers.google.com/tasks/v1/reference/tasks/list
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
const taskListId = this.getNodeParameter('task', i) as string;
|
||||
const options = this.getNodeParameter(
|
||||
const { showCompleted = true, showDeleted = false, showHidden = false, ...options } = this.getNodeParameter(
|
||||
'additionalFields',
|
||||
i,
|
||||
) as IDataObject;
|
||||
|
@ -187,15 +187,11 @@ export class GoogleTasks implements INodeType {
|
|||
if (options.dueMax) {
|
||||
qs.dueMax = options.dueMax as string;
|
||||
}
|
||||
if (options.showCompleted) {
|
||||
qs.showCompleted = options.showCompleted as boolean;
|
||||
}
|
||||
if (options.showDeleted) {
|
||||
qs.showDeleted = options.showDeleted as boolean;
|
||||
}
|
||||
if (options.showHidden) {
|
||||
qs.showHidden = options.showHidden as boolean;
|
||||
}
|
||||
|
||||
qs.showCompleted = showCompleted;
|
||||
qs.showDeleted = showDeleted;
|
||||
qs.showHidden = showHidden;
|
||||
|
||||
if (options.updatedMin) {
|
||||
qs.updatedMin = options.updatedMin as string;
|
||||
}
|
||||
|
|
|
@ -76,6 +76,16 @@ export const taskFields: INodeProperties[] = [
|
|||
type: 'string',
|
||||
default: '',
|
||||
description: 'Title of the task.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
|
@ -350,21 +360,21 @@ export const taskFields: INodeProperties[] = [
|
|||
name: 'showCompleted',
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
description: 'Flag indicating whether completed tasks are returned in the result',
|
||||
description: 'Flag indicating whether completed tasks are returned in the result. <strong>Show Hidden</strong> must also be True to show tasks completed in first party clients such as the web UI or Google\'s mobile apps.',
|
||||
},
|
||||
{
|
||||
displayName: 'Show Deleted',
|
||||
name: 'showDeleted',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Flag indicating whether deleted tasks are returned in the result',
|
||||
description: 'Flag indicating whether deleted tasks are returned in the result.',
|
||||
},
|
||||
{
|
||||
displayName: 'Show Hidden',
|
||||
name: 'showHidden',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Flag indicating whether hidden tasks are returned in the result',
|
||||
description: 'Flag indicating whether hidden tasks are returned in the result.',
|
||||
},
|
||||
{
|
||||
displayName: 'Updated Min',
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
JsonObject,
|
||||
NodeApiError,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
@ -412,17 +413,22 @@ export class GraphQL implements INodeType {
|
|||
} else {
|
||||
if (typeof response === 'string') {
|
||||
try {
|
||||
returnItems.push({ json: JSON.parse(response) });
|
||||
response = JSON.parse(response);
|
||||
} catch (error) {
|
||||
throw new NodeOperationError(this.getNode(), 'Response body is not valid JSON. Change "Response Format" to "String"');
|
||||
}
|
||||
} else {
|
||||
returnItems.push({ json: response });
|
||||
}
|
||||
|
||||
if (response.errors) {
|
||||
const message = response.errors?.map((error: IDataObject) => error.message).join(', ') || 'Unexpected error';
|
||||
throw new NodeApiError(this.getNode(), response.errors, { message });
|
||||
}
|
||||
|
||||
returnItems.push({ json: response });
|
||||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnItems.push({ json: { error: error.message } });
|
||||
returnItems.push({ json: { error: (error as JsonObject).message } });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
|
|
|
@ -161,6 +161,10 @@ export class HttpRequest implements INodeType {
|
|||
name: 'HEAD',
|
||||
value: 'HEAD',
|
||||
},
|
||||
{
|
||||
name: 'OPTIONS',
|
||||
value: 'OPTIONS',
|
||||
},
|
||||
{
|
||||
name: 'PATCH',
|
||||
value: 'PATCH',
|
||||
|
|
|
@ -133,7 +133,7 @@ export class If implements INodeType {
|
|||
value: 'smaller',
|
||||
},
|
||||
{
|
||||
name: 'Smaller Equal',
|
||||
name: 'Smaller or Equal',
|
||||
value: 'smallerEqual',
|
||||
},
|
||||
{
|
||||
|
@ -149,7 +149,7 @@ export class If implements INodeType {
|
|||
value: 'larger',
|
||||
},
|
||||
{
|
||||
name: 'Larger Equal',
|
||||
name: 'Larger or Equal',
|
||||
value: 'largerEqual',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
IDataObject,
|
||||
JsonObject,
|
||||
NodeApiError,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
@ -69,7 +70,7 @@ export async function jiraSoftwareCloudApiRequest(this: IHookFunctions | IExecut
|
|||
try {
|
||||
return await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,6 +120,49 @@ export function getId(url: string) {
|
|||
return url.split('/').pop();
|
||||
}
|
||||
|
||||
export function simplifyIssueOutput(responseData: {
|
||||
names: { [key: string]: string },
|
||||
fields: IDataObject,
|
||||
id: string,
|
||||
key: string,
|
||||
self: string
|
||||
}) {
|
||||
const mappedFields: IDataObject = {
|
||||
id: responseData.id,
|
||||
key: responseData.key,
|
||||
self: responseData.self,
|
||||
};
|
||||
// Sort custom fields last so we map them last
|
||||
const customField = /^customfield_\d+$/;
|
||||
const sortedFields: string[] = Object.keys(responseData.fields).sort((a, b) => {
|
||||
if (customField.test(a) && customField.test(b)) {
|
||||
return a > b ? 1 : -1;
|
||||
}
|
||||
if (customField.test(a)) {
|
||||
return 1;
|
||||
}
|
||||
if (customField.test(b)) {
|
||||
return -1;
|
||||
}
|
||||
return a > b ? 1 : -1;
|
||||
});
|
||||
for (const field of sortedFields) {
|
||||
if (responseData.names[field] in mappedFields) {
|
||||
let newField: string = responseData.names[field];
|
||||
let counter = 0;
|
||||
while (newField in mappedFields) {
|
||||
counter++;
|
||||
newField = `${responseData.names[field]}_${counter}`;
|
||||
}
|
||||
mappedFields[newField] = responseData.fields[field];
|
||||
} else {
|
||||
mappedFields[responseData.names[field] || field] = responseData.fields[field];
|
||||
}
|
||||
}
|
||||
|
||||
return mappedFields;
|
||||
}
|
||||
|
||||
export const allEvents = [
|
||||
'board_created',
|
||||
'board_updated',
|
||||
|
|
|
@ -530,6 +530,24 @@ export const issueFields: INodeProperties[] = [
|
|||
default: '',
|
||||
description: 'Issue Key',
|
||||
},
|
||||
{
|
||||
displayName: 'Simplify Output',
|
||||
name: 'simplifyOutput',
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'issue',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: `Return a simplified output of the issues fields.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
|
|
|
@ -18,12 +18,14 @@ import {
|
|||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
JsonObject,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
jiraSoftwareCloudApiRequest,
|
||||
jiraSoftwareCloudApiRequestAllItems,
|
||||
simplifyIssueOutput,
|
||||
validateJSON,
|
||||
} from './GenericFunctions';
|
||||
|
||||
|
@ -178,7 +180,7 @@ export class Jira implements INodeType {
|
|||
} catch (err) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: `Connection details not valid: ${err.message}`,
|
||||
message: `Connection details not valid: ${(err as JsonObject).message}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
|
@ -303,45 +305,29 @@ export class Jira implements INodeType {
|
|||
// Get all the users to display them to user so that he can
|
||||
// select them easily
|
||||
async getUsers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const jiraVersion = this.getCurrentNodeParameter('jiraVersion') as string;
|
||||
const query: IDataObject = {};
|
||||
let endpoint = '/api/2/users/search';
|
||||
|
||||
if (jiraVersion === 'server') {
|
||||
// the interface call must bring username
|
||||
const users = await jiraSoftwareCloudApiRequest.call(this, '/api/2/user/search', 'GET', {},
|
||||
{
|
||||
username: '\'',
|
||||
},
|
||||
);
|
||||
for (const user of users) {
|
||||
const userName = user.displayName;
|
||||
const userId = user.name;
|
||||
|
||||
returnData.push({
|
||||
name: userName,
|
||||
value: userId,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const users = await jiraSoftwareCloudApiRequest.call(this, '/api/2/users/search', 'GET');
|
||||
|
||||
for (const user of users) {
|
||||
const userName = user.displayName;
|
||||
const userId = user.accountId;
|
||||
|
||||
returnData.push({
|
||||
name: userName,
|
||||
value: userId,
|
||||
});
|
||||
}
|
||||
endpoint = '/api/2/user/search';
|
||||
query.username = '\'';
|
||||
}
|
||||
|
||||
returnData.sort((a, b) => {
|
||||
if (a.name < b.name) { return -1; }
|
||||
if (a.name > b.name) { return 1; }
|
||||
return 0;
|
||||
const users = await jiraSoftwareCloudApiRequest.call(this, endpoint, 'GET', {}, query);
|
||||
|
||||
return users.reduce((activeUsers: INodePropertyOptions[], user: IDataObject) => {
|
||||
if (user.active) {
|
||||
activeUsers.push({
|
||||
name: user.displayName as string,
|
||||
value: (user.accountId || user.name) as string,
|
||||
});
|
||||
}
|
||||
return activeUsers;
|
||||
}, []).sort((a: INodePropertyOptions, b: INodePropertyOptions) => {
|
||||
return a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1;
|
||||
});
|
||||
|
||||
return returnData;
|
||||
},
|
||||
|
||||
// Get all the groups to display them to user so that he can
|
||||
|
@ -418,10 +404,10 @@ export class Jira implements INodeType {
|
|||
for (const key of Object.keys(fields)) {
|
||||
const field = fields[key];
|
||||
if (field.schema && Object.keys(field.schema).includes('customId')) {
|
||||
returnData.push({
|
||||
name: field.name,
|
||||
value: field.key || field.fieldId,
|
||||
});
|
||||
returnData.push({
|
||||
name: field.name,
|
||||
value: field.key || field.fieldId,
|
||||
});
|
||||
}
|
||||
}
|
||||
return returnData;
|
||||
|
@ -642,6 +628,7 @@ export class Jira implements INodeType {
|
|||
if (operation === 'get') {
|
||||
for (let i = 0; i < length; i++) {
|
||||
const issueKey = this.getNodeParameter('issueKey', i) as string;
|
||||
const simplifyOutput = this.getNodeParameter('simplifyOutput', i) as boolean;
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
if (additionalFields.fields) {
|
||||
qs.fields = additionalFields.fields as string;
|
||||
|
@ -652,6 +639,9 @@ export class Jira implements INodeType {
|
|||
if (additionalFields.expand) {
|
||||
qs.expand = additionalFields.expand as string;
|
||||
}
|
||||
if (simplifyOutput) {
|
||||
qs.expand = `${qs.expand || ''},names`;
|
||||
}
|
||||
if (additionalFields.properties) {
|
||||
qs.properties = additionalFields.properties as string;
|
||||
}
|
||||
|
@ -659,7 +649,12 @@ export class Jira implements INodeType {
|
|||
qs.updateHistory = additionalFields.updateHistory as string;
|
||||
}
|
||||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/issue/${issueKey}`, 'GET', {}, qs);
|
||||
returnData.push(responseData);
|
||||
|
||||
if (simplifyOutput) {
|
||||
returnData.push(simplifyIssueOutput(responseData));
|
||||
} else {
|
||||
returnData.push(responseData);
|
||||
}
|
||||
}
|
||||
}
|
||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-search-post
|
||||
|
@ -689,7 +684,7 @@ export class Jira implements INodeType {
|
|||
responseData = await jiraSoftwareCloudApiRequest.call(this, `/api/2/search`, 'POST', body);
|
||||
responseData = responseData.issues;
|
||||
}
|
||||
returnData.push.apply(returnData, responseData);
|
||||
returnData.push(...responseData);
|
||||
}
|
||||
}
|
||||
//https://developer.atlassian.com/cloud/jira/platform/rest/v2/#api-rest-api-2-issue-issueIdOrKey-changelog-get
|
||||
|
|
21
packages/nodes-base/nodes/KoBoToolbox/KoBoToolbox.node.json
Normal file
21
packages/nodes-base/nodes/KoBoToolbox/KoBoToolbox.node.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"node": "n8n-nodes-base.koBoToolbox",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Communication",
|
||||
"Data & Storage"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/kobotoolbox"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.koBoToolbox/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"node": "n8n-nodes-base.koBoToolboxTrigger",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Communication",
|
||||
"Data & Storage"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/kobotoolbox"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.koBoToolboxTrigger/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
20
packages/nodes-base/nodes/Linear/Linear.node.json
Normal file
20
packages/nodes-base/nodes/Linear/Linear.node.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"node": "n8n-nodes-base.linear",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Productivity"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/credentials/linear/"
|
||||
}
|
||||
],
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.linear/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -10,7 +10,11 @@ import {
|
|||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject, JsonObject, NodeApiError,
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
JsonObject,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function mauticApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body: any = {}, query?: IDataObject, uri?: string): Promise<any> { // tslint:disable-line:no-any
|
||||
|
@ -31,19 +35,21 @@ export async function mauticApiRequest(this: IHookFunctions | IExecuteFunctions
|
|||
|
||||
if (authenticationMethod === 'credentials') {
|
||||
const credentials = await this.getCredentials('mauticApi') as IDataObject;
|
||||
const baseUrl = credentials!.url as string;
|
||||
|
||||
const base64Key = Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64');
|
||||
|
||||
options.headers!.Authorization = `Basic ${base64Key}`;
|
||||
|
||||
options.uri = `${credentials.url}${options.uri}`;
|
||||
options.uri = `${baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl}${options.uri}`;
|
||||
|
||||
//@ts-ignore
|
||||
returnData = await this.helpers.request(options);
|
||||
} else {
|
||||
const credentials = await this.getCredentials('mauticOAuth2Api') as IDataObject;
|
||||
const baseUrl = credentials!.url as string;
|
||||
|
||||
options.uri = `${credentials.url}${options.uri}`;
|
||||
options.uri = `${baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl}${options.uri}`;
|
||||
//@ts-ignore
|
||||
returnData = await this.helpers.requestOAuth2.call(this, 'mauticOAuth2Api', options, { includeCredentialsOnRefreshOnBody: true });
|
||||
}
|
||||
|
@ -96,3 +102,30 @@ export function validateJSON(json: string | undefined): any { // tslint:disable-
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function validateCredentials(this: ICredentialTestFunctions, decryptedCredentials: ICredentialDataDecryptedObject): Promise<any> { // tslint:disable-line:no-any
|
||||
const credentials = decryptedCredentials;
|
||||
|
||||
const {
|
||||
url,
|
||||
username,
|
||||
password,
|
||||
} = credentials as {
|
||||
url: string,
|
||||
username: string,
|
||||
password: string,
|
||||
};
|
||||
|
||||
const base64Key = Buffer.from(`${username}:${password}`).toString('base64');
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: `Basic ${base64Key}`,
|
||||
},
|
||||
uri: url.endsWith('/') ? `${url}api/users/self` : `${url}/api/users/self`,
|
||||
json: true,
|
||||
};
|
||||
|
||||
return await this.helpers.request(options);
|
||||
}
|
||||
|
|
|
@ -3,8 +3,12 @@ import {
|
|||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialDataDecryptedObject,
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
ILoadOptionsFunctions,
|
||||
INodeCredentialTestResult,
|
||||
INodeExecutionData,
|
||||
INodePropertyOptions,
|
||||
INodeType,
|
||||
|
@ -17,6 +21,7 @@ import {
|
|||
import {
|
||||
mauticApiRequest,
|
||||
mauticApiRequestAllItems,
|
||||
validateCredentials,
|
||||
validateJSON,
|
||||
} from './GenericFunctions';
|
||||
|
||||
|
@ -80,6 +85,7 @@ export class Mautic implements INodeType {
|
|||
],
|
||||
},
|
||||
},
|
||||
testedBy: 'mauticCredentialTest',
|
||||
},
|
||||
{
|
||||
name: 'mauticOAuth2Api',
|
||||
|
@ -166,6 +172,29 @@ export class Mautic implements INodeType {
|
|||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
async mauticCredentialTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<INodeCredentialTestResult> {
|
||||
try {
|
||||
let responseData;
|
||||
responseData = await validateCredentials.call(this, credential.data as ICredentialDataDecryptedObject);
|
||||
if (responseData.id) {
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Authentication successful!',
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: 'Invalid credentials',
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 'Error',
|
||||
message: 'Invalid credentials',
|
||||
};
|
||||
},
|
||||
},
|
||||
loadOptions: {
|
||||
// Get all the available companies to display them to user so that he can
|
||||
// select them easily
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const chatMessageOperations: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'chatMessage',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a message',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a message',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all messages',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
];
|
||||
|
||||
export const chatMessageFields: INodeProperties[] = [
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* chatMessage:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Chat ID',
|
||||
name: 'chatId',
|
||||
required: true,
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChats',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'chatMessage',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Chat ID',
|
||||
},
|
||||
{
|
||||
displayName: 'Message Type',
|
||||
name: 'messageType',
|
||||
required: true,
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Text',
|
||||
value: 'text',
|
||||
},
|
||||
{
|
||||
name: 'HTML',
|
||||
value: 'html',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'chatMessage',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'The type of the content',
|
||||
},
|
||||
{
|
||||
displayName: 'Message',
|
||||
name: 'message',
|
||||
required: true,
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'chatMessage',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'The content of the item.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* chatMessage:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Message ID',
|
||||
name: 'messageId',
|
||||
required: true,
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'chatMessage',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* chatMessage:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Chat ID',
|
||||
name: 'chatId',
|
||||
required: true,
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getChats',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'chatMessage',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Chat ID',
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'chatMessage',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit.',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'chatMessage',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 500,
|
||||
},
|
||||
default: 100,
|
||||
description: 'How many results to return.',
|
||||
},
|
||||
];
|
|
@ -26,6 +26,11 @@ import {
|
|||
channelMessageOperations,
|
||||
} from './ChannelMessageDescription';
|
||||
|
||||
import {
|
||||
chatMessageFields,
|
||||
chatMessageOperations,
|
||||
} from './ChatMessageDescription';
|
||||
|
||||
import {
|
||||
taskFields,
|
||||
taskOperations,
|
||||
|
@ -65,6 +70,10 @@ export class MicrosoftTeams implements INodeType {
|
|||
name: 'Channel Message (Beta)',
|
||||
value: 'channelMessage',
|
||||
},
|
||||
{
|
||||
name: 'Chat Message',
|
||||
value: 'chatMessage',
|
||||
},
|
||||
{
|
||||
name: 'Task',
|
||||
value: 'task',
|
||||
|
@ -79,6 +88,8 @@ export class MicrosoftTeams implements INodeType {
|
|||
/// MESSAGE
|
||||
...channelMessageOperations,
|
||||
...channelMessageFields,
|
||||
...chatMessageOperations,
|
||||
...chatMessageFields,
|
||||
///TASK
|
||||
...taskOperations,
|
||||
...taskFields,
|
||||
|
@ -189,6 +200,29 @@ export class MicrosoftTeams implements INodeType {
|
|||
}
|
||||
return returnData;
|
||||
},
|
||||
// Get all the chats to display them to user so that they can
|
||||
// select them easily
|
||||
async getChats(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const qs: IDataObject = {
|
||||
$expand: 'members',
|
||||
};
|
||||
const { value } = await microsoftApiRequest.call(this, 'GET', '/v1.0/chats', {}, qs);
|
||||
for (const chat of value) {
|
||||
if (!chat.topic) {
|
||||
chat.topic = chat.members
|
||||
.filter((member: IDataObject) => member.displayName)
|
||||
.map((member: IDataObject) => member.displayName).join(', ');
|
||||
}
|
||||
const chatName = `${chat.topic || '(no title) - ' + chat.id} (${chat.chatType})`;
|
||||
const chatId = chat.id;
|
||||
returnData.push({
|
||||
name: chatName,
|
||||
value: chatId,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -298,6 +332,39 @@ export class MicrosoftTeams implements INodeType {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (resource === 'chatMessage') {
|
||||
// https://docs.microsoft.com/en-us/graph/api/channel-post-messages?view=graph-rest-1.0&tabs=http
|
||||
if (operation === 'create') {
|
||||
const chatId = this.getNodeParameter('chatId', i) as string;
|
||||
const messageType = this.getNodeParameter('messageType', i) as string;
|
||||
const message = this.getNodeParameter('message', i) as string;
|
||||
const body: IDataObject = {
|
||||
body: {
|
||||
contentType: messageType,
|
||||
content: message,
|
||||
},
|
||||
};
|
||||
responseData = await microsoftApiRequest.call(this, 'POST', `/v1.0/chats/${chatId}/messages`, body);
|
||||
}
|
||||
// https://docs.microsoft.com/en-us/graph/api/chat-list-messages?view=graph-rest-1.0&tabs=http
|
||||
if (operation === 'get') {
|
||||
const chatId = this.getNodeParameter('chatId', i) as string;
|
||||
const messageId = this.getNodeParameter('messageId', i) as string;
|
||||
responseData = await microsoftApiRequest.call(this, 'GET', `/v1.0/chats/${chatId}/messages/${messageId}`);
|
||||
}
|
||||
// https://docs.microsoft.com/en-us/graph/api/chat-list-messages?view=graph-rest-1.0&tabs=http
|
||||
if (operation === 'getAll') {
|
||||
const chatId = this.getNodeParameter('chatId', i) as string;
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
if (returnAll) {
|
||||
responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/chats/${chatId}/messages`);
|
||||
} else {
|
||||
qs.limit = this.getNodeParameter('limit', i) as number;
|
||||
responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/chats/${chatId}/messages`, {});
|
||||
responseData = responseData.splice(0, qs.limit);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (resource === 'task') {
|
||||
//https://docs.microsoft.com/en-us/graph/api/planner-post-tasks?view=graph-rest-1.0&tabs=http
|
||||
if (operation === 'create') {
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject, NodeApiError, NodeOperationError,
|
||||
IDataObject, JsonObject, NodeApiError, NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
/**
|
||||
|
@ -47,6 +47,6 @@ export async function moceanApiRequest(this: IHookFunctions | IExecuteFunctions,
|
|||
try {
|
||||
return await this.helpers.request(options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
throw new NodeApiError(this.getNode(), (error as JsonObject));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
import { IExecuteFunctions } from 'n8n-core';
|
||||
import {
|
||||
IExecuteFunctions,
|
||||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
ICredentialsDecrypted,
|
||||
ICredentialTestFunctions,
|
||||
IDataObject,
|
||||
INodeCredentialTestResult,
|
||||
INodeExecutionData,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
JsonObject,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {moceanApiRequest} from './GenericFunctions';
|
||||
|
||||
import {
|
||||
moceanApiRequest,
|
||||
} from './GenericFunctions';
|
||||
|
||||
export class Mocean implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Mocean',
|
||||
name: 'mocean',
|
||||
icon: 'file:mocean.png',
|
||||
subtitle: `={{$parameter["operation"] + ": " + $parameter["resource"]}}`,
|
||||
icon: 'file:mocean.svg',
|
||||
group: ['transform'],
|
||||
version: 1,
|
||||
description: 'Send SMS and voice messages via Mocean',
|
||||
|
@ -27,6 +36,7 @@ export class Mocean implements INodeType {
|
|||
{
|
||||
name: 'moceanApi',
|
||||
required: true,
|
||||
testedBy: 'moceanApiTest',
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
|
@ -36,7 +46,8 @@ export class Mocean implements INodeType {
|
|||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
options:[
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'SMS',
|
||||
value: 'sms',
|
||||
|
@ -52,6 +63,7 @@ export class Mocean implements INodeType {
|
|||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -68,7 +80,7 @@ export class Mocean implements INodeType {
|
|||
},
|
||||
],
|
||||
default: 'send',
|
||||
description: 'The operation to perform.',
|
||||
description: 'Operation to perform',
|
||||
},
|
||||
{
|
||||
displayName: 'From',
|
||||
|
@ -88,7 +100,7 @@ export class Mocean implements INodeType {
|
|||
],
|
||||
},
|
||||
},
|
||||
description: 'The number to which to send the message',
|
||||
description: 'Number to which to send the message',
|
||||
},
|
||||
|
||||
{
|
||||
|
@ -109,14 +121,14 @@ export class Mocean implements INodeType {
|
|||
],
|
||||
},
|
||||
},
|
||||
description: 'The number from which to send the message',
|
||||
description: 'Number from which to send the message',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Language',
|
||||
name: 'language',
|
||||
type: 'options',
|
||||
options:[
|
||||
options: [
|
||||
{
|
||||
name: 'Chinese Mandarin (China)',
|
||||
value: 'cmn-CN',
|
||||
|
@ -169,10 +181,66 @@ export class Mocean implements INodeType {
|
|||
],
|
||||
},
|
||||
},
|
||||
description: 'The message to send',
|
||||
description: 'Message to send',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'send',
|
||||
],
|
||||
resource: [
|
||||
'sms',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
displayName: 'Delivery Report URL',
|
||||
name: 'dlrUrl',
|
||||
type: 'string',
|
||||
default: '',
|
||||
placeholder: '',
|
||||
description: 'Delivery report URL',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
methods = {
|
||||
credentialTest: {
|
||||
async moceanApiTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<INodeCredentialTestResult> {
|
||||
const credentials = credential.data;
|
||||
const query: IDataObject = {};
|
||||
query['mocean-api-key'] = credentials!['mocean-api-key'];
|
||||
query['mocean-api-secret'] = credentials!['mocean-api-secret'];
|
||||
|
||||
const options = {
|
||||
method: 'GET',
|
||||
qs: query,
|
||||
uri: `https://rest.moceanapi.com/rest/2/account/balance`,
|
||||
json: true,
|
||||
};
|
||||
try {
|
||||
await this.helpers.request!(options);
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
message: `Connection details not valid: ${(error as JsonObject).message}`,
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: 'OK',
|
||||
message: 'Authentication successful!',
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
@ -185,6 +253,7 @@ export class Mocean implements INodeType {
|
|||
let requesetMethod: string;
|
||||
let resource: string;
|
||||
let text: string;
|
||||
let dlrUrl: string;
|
||||
let dataKey: string;
|
||||
// For Post
|
||||
let body: IDataObject;
|
||||
|
@ -196,7 +265,7 @@ export class Mocean implements INodeType {
|
|||
qs = {};
|
||||
try {
|
||||
resource = this.getNodeParameter('resource', itemIndex, '') as string;
|
||||
operation = this.getNodeParameter('operation',itemIndex,'') as string;
|
||||
operation = this.getNodeParameter('operation', itemIndex, '') as string;
|
||||
text = this.getNodeParameter('message', itemIndex, '') as string;
|
||||
requesetMethod = 'POST';
|
||||
body['mocean-from'] = this.getNodeParameter('from', itemIndex, '') as string;
|
||||
|
@ -215,16 +284,21 @@ export class Mocean implements INodeType {
|
|||
dataKey = 'voice';
|
||||
body['mocean-command'] = JSON.stringify(command);
|
||||
endpoint = '/rest/2/voice/dial';
|
||||
} else if(resource === 'sms') {
|
||||
} else if (resource === 'sms') {
|
||||
dlrUrl = this.getNodeParameter('options.dlrUrl', itemIndex, '') as string;
|
||||
dataKey = 'messages';
|
||||
body['mocean-text'] = text;
|
||||
if (dlrUrl !== '') {
|
||||
body['mocean-dlr-url'] = dlrUrl;
|
||||
body['mocean-dlr-mask'] = '1';
|
||||
}
|
||||
endpoint = '/rest/2/sms';
|
||||
} else {
|
||||
throw new NodeOperationError(this.getNode(), `Unknown resource ${resource}`);
|
||||
}
|
||||
|
||||
if (operation === 'send') {
|
||||
const responseData = await moceanApiRequest.call(this,requesetMethod,endpoint,body,qs);
|
||||
const responseData = await moceanApiRequest.call(this, requesetMethod, endpoint, body, qs);
|
||||
|
||||
for (const item of responseData[dataKey] as IDataObject[]) {
|
||||
item.type = resource;
|
||||
|
@ -236,7 +310,7 @@ export class Mocean implements INodeType {
|
|||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.message });
|
||||
returnData.push({ error: (error as JsonObject).message });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 669 B |
24
packages/nodes-base/nodes/Mocean/mocean.svg
Normal file
24
packages/nodes-base/nodes/Mocean/mocean.svg
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.16, written by Peter Selinger 2001-2019
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
|
||||
fill="#0748A6" stroke="none">
|
||||
<path d="M0 2560 l0 -2560 2560 0 2560 0 0 2560 0 2560 -2560 0 -2560 0 0
|
||||
-2560z m1544 1460 c204 -20 306 -107 374 -320 54 -167 640 -1761 648 -1762 5
|
||||
0 46 96 90 213 171 450 574 1519 590 1566 30 90 85 182 137 228 81 74 113 80
|
||||
447 80 l285 0 45 -25 c24 -14 58 -45 75 -68 l30 -44 3 -1375 c2 -1261 1 -1378
|
||||
-14 -1394 -32 -35 -82 -44 -259 -44 -180 0 -227 8 -261 46 -18 20 -19 67 -24
|
||||
1233 l-5 1211 -47 -130 c-26 -71 -221 -615 -432 -1208 -212 -594 -393 -1088
|
||||
-403 -1099 -44 -49 -66 -53 -268 -53 -174 0 -194 2 -237 22 -25 11 -53 32 -62
|
||||
45 -8 12 -82 219 -165 458 -329 959 -660 1916 -669 1940 -8 17 -11 -347 -11
|
||||
-1181 -1 -1194 -1 -1207 -21 -1232 -34 -43 -79 -52 -270 -52 -155 0 -180 2
|
||||
-215 20 -22 11 -45 31 -52 45 -10 20 -12 306 -10 1370 l2 1346 26 49 c31 62
|
||||
92 107 157 116 74 10 407 9 516 -1z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -96,13 +96,12 @@ export async function apiRequestAllItems(this: IHookFunctions | IExecuteFunction
|
|||
|
||||
do {
|
||||
responseData = await apiRequest.call(this, method, endpoint, body, query);
|
||||
|
||||
returnData.push(...responseData);
|
||||
|
||||
query.offset += query.limit;
|
||||
|
||||
} while (
|
||||
responseData.length === 0
|
||||
responseData.length !== 0
|
||||
);
|
||||
|
||||
return returnData;
|
||||
|
|
|
@ -16,5 +16,8 @@
|
|||
"url": "https://docs.n8n.io/nodes/n8n-nodes-base.odoo/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"alias": [
|
||||
"ERP"
|
||||
]
|
||||
}
|
|
@ -4,7 +4,7 @@ import {
|
|||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject, NodeApiError, NodeOperationError,
|
||||
IDataObject, JsonObject, NodeApiError, NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
|
@ -45,7 +45,7 @@ export async function redditApiRequest(
|
|||
try {
|
||||
return await this.helpers.requestOAuth2.call(this, 'redditOAuth2Api', options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -53,7 +53,7 @@ export async function redditApiRequest(
|
|||
try {
|
||||
return await this.helpers.request.call(this, options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,6 +60,11 @@ export const profileFields: INodeProperties[] = [
|
|||
value: 'prefs',
|
||||
description: 'Return the settings preferences of the logged-in user',
|
||||
},
|
||||
{
|
||||
name: 'Saved',
|
||||
value: 'saved',
|
||||
description: 'Return the saved posts for the user',
|
||||
},
|
||||
{
|
||||
name: 'Trophies',
|
||||
value: 'trophies',
|
||||
|
@ -77,4 +82,51 @@ export const profileFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Return all results.',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'profile',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
details: [
|
||||
'saved',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
default: 100,
|
||||
description: 'The number of results to return.',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 100,
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'profile',
|
||||
],
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
details: [
|
||||
'saved',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
@ -352,7 +352,15 @@ export class Reddit implements INodeType {
|
|||
|
||||
const details = this.getNodeParameter('details', i) as string;
|
||||
const endpoint = `api/v1/${endpoints[details]}`;
|
||||
responseData = await redditApiRequest.call(this, 'GET', endpoint, {});
|
||||
let username;
|
||||
|
||||
if (details === 'saved') {
|
||||
({ name: username } = await redditApiRequest.call(this, 'GET', `api/v1/me`, {}));
|
||||
}
|
||||
|
||||
responseData = details === 'saved'
|
||||
? await handleListing.call(this, i, `user/${username}/saved.json`)
|
||||
: await redditApiRequest.call(this, 'GET', endpoint, {});
|
||||
|
||||
if (details === 'identity') {
|
||||
responseData = responseData.features;
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
"codexVersion": "1.0",
|
||||
"categories": [
|
||||
"Communication",
|
||||
"Development"
|
||||
"Development",
|
||||
"Data & Storage"
|
||||
],
|
||||
"resources": {
|
||||
"credentialDocumentation": [
|
||||
|
|
|
@ -2184,6 +2184,9 @@ export class Salesforce implements INodeType {
|
|||
if (additionalFields.reason !== undefined) {
|
||||
body.Reason = additionalFields.reason as string;
|
||||
}
|
||||
if (additionalFields.status !== undefined) {
|
||||
body.Status = additionalFields.status as string;
|
||||
}
|
||||
if (additionalFields.owner !== undefined) {
|
||||
body.OwnerId = additionalFields.owner as string;
|
||||
}
|
||||
|
@ -2248,6 +2251,9 @@ export class Salesforce implements INodeType {
|
|||
if (updateFields.reason !== undefined) {
|
||||
body.Reason = updateFields.reason as string;
|
||||
}
|
||||
if (updateFields.status !== undefined) {
|
||||
body.Status = updateFields.status as string;
|
||||
}
|
||||
if (updateFields.owner !== undefined) {
|
||||
body.OwnerId = updateFields.owner as string;
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ export const businessServiceOperations: INodeProperties[] = [
|
|||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -44,7 +45,7 @@ export const businessServiceFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
|
@ -99,17 +100,19 @@ export const businessServiceFields: INodeProperties[] = [
|
|||
name: 'sysparm_fields',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getColumns',
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'A list of fields to return',
|
||||
hint: 'String of comma separated values or an array of strings can be set in an expression',
|
||||
},
|
||||
{
|
||||
displayName: 'Filter',
|
||||
name: 'sysparm_query',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Values',
|
||||
|
|
|
@ -7,6 +7,7 @@ export const configurationItemsOperations: INodeProperties[] = [
|
|||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -44,7 +45,7 @@ export const configurationItemsFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
|
@ -68,7 +69,7 @@ export const configurationItemsFields: INodeProperties[] = [
|
|||
maxValue: 500,
|
||||
},
|
||||
default: 50,
|
||||
description: 'The max number of results to return',
|
||||
description: 'Max number of results to return',
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
|
@ -99,17 +100,19 @@ export const configurationItemsFields: INodeProperties[] = [
|
|||
name: 'sysparm_fields',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getColumns',
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'A list of fields to return',
|
||||
hint: 'String of comma separated values or an array of strings can be set in an expression',
|
||||
},
|
||||
{
|
||||
displayName: 'Filter',
|
||||
name: 'sysparm_query',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Values',
|
||||
|
|
|
@ -7,6 +7,7 @@ export const departmentOperations: INodeProperties[] = [
|
|||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -44,7 +45,7 @@ export const departmentFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
|
@ -99,17 +100,19 @@ export const departmentFields: INodeProperties[] = [
|
|||
name: 'sysparm_fields',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getColumns',
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'A list of fields to return',
|
||||
hint: 'String of comma separated values or an array of strings can be set in an expression',
|
||||
},
|
||||
{
|
||||
displayName: 'Filter',
|
||||
name: 'sysparm_query',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Values',
|
||||
|
|
|
@ -7,6 +7,7 @@ export const dictionaryOperations: INodeProperties[] = [
|
|||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -44,7 +45,7 @@ export const dictionaryFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
|
@ -99,17 +100,19 @@ export const dictionaryFields: INodeProperties[] = [
|
|||
name: 'sysparm_fields',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getColumns',
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'A list of fields to return',
|
||||
hint: 'String of comma separated values or an array of strings can be set in an expression',
|
||||
},
|
||||
{
|
||||
displayName: 'Filter',
|
||||
name: 'sysparm_query',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Values',
|
||||
|
|
|
@ -10,15 +10,25 @@ import {
|
|||
import {
|
||||
IDataObject,
|
||||
INodePropertyOptions,
|
||||
JsonObject,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function serviceNowApiRequest(this: IExecuteFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, option: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
|
||||
const credentials = await this.getCredentials('serviceNowOAuth2Api');
|
||||
const headers = {} as IDataObject;
|
||||
const authenticationMethod = this.getNodeParameter('authentication', 0, 'oAuth2') as string;
|
||||
|
||||
let credentials;
|
||||
|
||||
if (authenticationMethod === 'basicAuth') {
|
||||
credentials = await this.getCredentials('serviceNowBasicApi');
|
||||
} else {
|
||||
credentials = await this.getCredentials('serviceNowOAuth2Api');
|
||||
}
|
||||
|
||||
const options: OptionsWithUri = {
|
||||
headers: {},
|
||||
headers,
|
||||
method,
|
||||
qs,
|
||||
body,
|
||||
|
@ -38,11 +48,11 @@ export async function serviceNowApiRequest(this: IExecuteFunctions | ILoadOption
|
|||
}
|
||||
|
||||
try {
|
||||
|
||||
return await this.helpers.requestOAuth2!.call(this, 'serviceNowOAuth2Api', options);
|
||||
const credentialType = authenticationMethod === 'oAuth2' ? 'serviceNowOAuth2Api' : 'serviceNowBasicApi';
|
||||
return await this.helpers.requestWithAuthentication.call(this, credentialType, options);
|
||||
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
throw new NodeApiError(this.getNode(), (error as JsonObject));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ export const incidentOperations: INodeProperties[] = [
|
|||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -85,19 +86,21 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'assigned_to',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getUsers',
|
||||
loadOptionsDependsOn: [
|
||||
'additionalFields.assignment_group',
|
||||
],
|
||||
},
|
||||
default: '',
|
||||
description: 'Which user is the incident assigned to. Requires the selection of an assignment group',
|
||||
description: 'Which user is the incident assigned to. Requires the selection of an assignment group.',
|
||||
},
|
||||
{
|
||||
displayName: 'Assignment Group',
|
||||
name: 'assignment_group',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getAssignmentGroups',
|
||||
},
|
||||
default: '',
|
||||
|
@ -108,6 +111,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'business_service',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getBusinessServices',
|
||||
},
|
||||
default: '',
|
||||
|
@ -125,6 +129,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'category',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getIncidentCategories',
|
||||
},
|
||||
default: '',
|
||||
|
@ -142,9 +147,10 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'cmdb_ci',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getConfigurationItems',
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'Configuration Items, \'cmdb_ci\' in metadata',
|
||||
},
|
||||
{
|
||||
|
@ -197,7 +203,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
value: 1,
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
default: 1,
|
||||
description: 'The impact of the incident',
|
||||
},
|
||||
{
|
||||
|
@ -205,16 +211,18 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'close_code',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getIncidentResolutionCodes',
|
||||
},
|
||||
default: '',
|
||||
description: 'The resolution code of the incident. \'close_code\' in metadata',
|
||||
description: 'The resolution code of the incident, \'close_code\' in metadata',
|
||||
},
|
||||
{
|
||||
displayName: 'State',
|
||||
name: 'state',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getIncidentStates',
|
||||
},
|
||||
default: '',
|
||||
|
@ -225,6 +233,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'subcategory',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getIncidentSubcategories',
|
||||
loadOptionsDependsOn: [
|
||||
'additionalFields.category',
|
||||
|
@ -251,7 +260,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
value: 1,
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
default: 1,
|
||||
description: 'The urgency of the incident',
|
||||
},
|
||||
],
|
||||
|
@ -275,7 +284,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
|
@ -330,17 +339,19 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'sysparm_fields',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getColumns',
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'A list of fields to return',
|
||||
hint: 'String of comma separated values or an array of strings can be set in an expression',
|
||||
},
|
||||
{
|
||||
displayName: 'Filter',
|
||||
name: 'sysparm_query',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Values',
|
||||
|
@ -417,10 +428,12 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'sysparm_fields',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getColumns',
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'A list of fields to return',
|
||||
hint: 'String of comma separated values or an array of strings can be set in an expression',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Values',
|
||||
|
@ -489,19 +502,21 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'assigned_to',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getUsers',
|
||||
loadOptionsDependsOn: [
|
||||
'additionalFields.assignment_group',
|
||||
],
|
||||
},
|
||||
default: '',
|
||||
description: 'Which user is the incident assigned to. Requires the selection of an assignment group',
|
||||
description: 'Which user is the incident assigned to. Requires the selection of an assignment group.',
|
||||
},
|
||||
{
|
||||
displayName: 'Assignment Group',
|
||||
name: 'assignment_group',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getAssignmentGroups',
|
||||
},
|
||||
default: '',
|
||||
|
@ -512,6 +527,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'business_service',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getBusinessServices',
|
||||
},
|
||||
default: '',
|
||||
|
@ -529,6 +545,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'category',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getIncidentCategories',
|
||||
},
|
||||
default: '',
|
||||
|
@ -546,9 +563,10 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'cmdb_ci',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getConfigurationItems',
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'Configuration Items, \'cmdb_ci\' in metadata',
|
||||
},
|
||||
{
|
||||
|
@ -601,7 +619,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
value: 1,
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
default: 1,
|
||||
description: 'The impact of the incident',
|
||||
},
|
||||
{
|
||||
|
@ -609,9 +627,11 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'close_code',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getIncidentResolutionCodes',
|
||||
},
|
||||
default: '',
|
||||
// nodelinter-ignore-next-line
|
||||
description: 'The resolution code of the incident. \'close_code\' in metadata',
|
||||
},
|
||||
{
|
||||
|
@ -619,6 +639,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'hold_reason',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getIncidentHoldReasons',
|
||||
},
|
||||
default: '',
|
||||
|
@ -629,6 +650,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'state',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getIncidentStates',
|
||||
},
|
||||
default: '',
|
||||
|
@ -639,6 +661,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
name: 'subcategory',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
// nodelinter-ignore-next-line
|
||||
loadOptionsMethod: 'getIncidentSubcategories',
|
||||
loadOptionsDependsOn: [
|
||||
'additionalFields.category',
|
||||
|
@ -665,7 +688,7 @@ export const incidentFields: INodeProperties[] = [
|
|||
value: 1,
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
default: 1,
|
||||
description: 'The urgency of the incident',
|
||||
},
|
||||
],
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
JsonObject,
|
||||
NodeOperationError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
|
@ -82,13 +83,49 @@ export class ServiceNow implements INodeType {
|
|||
{
|
||||
name: 'serviceNowOAuth2Api',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
authentication: [
|
||||
'oAuth2',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'serviceNowBasicApi',
|
||||
required: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
authentication: [
|
||||
'basicAuth',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Authentication',
|
||||
name: 'authentication',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Basic Auth',
|
||||
value: 'basicAuth',
|
||||
},
|
||||
{
|
||||
name: 'OAuth2',
|
||||
value: 'oAuth2',
|
||||
},
|
||||
],
|
||||
default: 'oAuth2',
|
||||
description: 'Authentication method to use',
|
||||
},
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
options: [
|
||||
{
|
||||
name: 'Business Service',
|
||||
|
@ -427,7 +464,7 @@ export class ServiceNow implements INodeType {
|
|||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
qs = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if (qs.sysparm_fields) {
|
||||
if (qs.sysparm_fields && typeof qs.sysparm_fields !== 'string') {
|
||||
qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
|
||||
}
|
||||
|
||||
|
@ -448,7 +485,7 @@ export class ServiceNow implements INodeType {
|
|||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
qs = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if (qs.sysparm_fields) {
|
||||
if (qs.sysparm_fields && typeof qs.sysparm_fields !== 'string') {
|
||||
qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
|
||||
}
|
||||
|
||||
|
@ -469,7 +506,7 @@ export class ServiceNow implements INodeType {
|
|||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
qs = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if (qs.sysparm_fields) {
|
||||
if (qs.sysparm_fields && typeof qs.sysparm_fields !== 'string') {
|
||||
qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
|
||||
}
|
||||
|
||||
|
@ -490,7 +527,7 @@ export class ServiceNow implements INodeType {
|
|||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
qs = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if (qs.sysparm_fields) {
|
||||
if (qs.sysparm_fields && typeof qs.sysparm_fields !== 'string') {
|
||||
qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
|
||||
}
|
||||
|
||||
|
@ -529,7 +566,7 @@ export class ServiceNow implements INodeType {
|
|||
const id = this.getNodeParameter('id', i) as string;
|
||||
qs = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if (qs.sysparm_fields) {
|
||||
if (qs.sysparm_fields && typeof qs.sysparm_fields !== 'string') {
|
||||
qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
|
||||
}
|
||||
|
||||
|
@ -541,7 +578,7 @@ export class ServiceNow implements INodeType {
|
|||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
qs = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if (qs.sysparm_fields) {
|
||||
if (qs.sysparm_fields && typeof qs.sysparm_fields !== 'string') {
|
||||
qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
|
||||
}
|
||||
|
||||
|
@ -604,7 +641,7 @@ export class ServiceNow implements INodeType {
|
|||
const id = this.getNodeParameter('id', i) as string;
|
||||
qs = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if (qs.sysparm_fields) {
|
||||
if (qs.sysparm_fields && typeof qs.sysparm_fields !== 'string') {
|
||||
qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
|
||||
}
|
||||
|
||||
|
@ -617,7 +654,7 @@ export class ServiceNow implements INodeType {
|
|||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
qs = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if (qs.sysparm_fields) {
|
||||
if (qs.sysparm_fields && typeof qs.sysparm_fields !== 'string') {
|
||||
qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
|
||||
}
|
||||
|
||||
|
@ -679,7 +716,7 @@ export class ServiceNow implements INodeType {
|
|||
const getOption = this.getNodeParameter('getOption', i) as string;
|
||||
qs = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if (qs.sysparm_fields) {
|
||||
if (qs.sysparm_fields && typeof qs.sysparm_fields !== 'string') {
|
||||
qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
|
||||
}
|
||||
|
||||
|
@ -700,7 +737,7 @@ export class ServiceNow implements INodeType {
|
|||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
qs = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if (qs.sysparm_fields) {
|
||||
if (qs.sysparm_fields && typeof qs.sysparm_fields !== 'string') {
|
||||
qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
|
||||
}
|
||||
|
||||
|
@ -730,7 +767,7 @@ export class ServiceNow implements INodeType {
|
|||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
qs = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if (qs.sysparm_fields) {
|
||||
if (qs.sysparm_fields && typeof qs.sysparm_fields !== 'string') {
|
||||
qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
|
||||
}
|
||||
|
||||
|
@ -751,7 +788,7 @@ export class ServiceNow implements INodeType {
|
|||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
qs = this.getNodeParameter('options', i) as IDataObject;
|
||||
|
||||
if (qs.sysparm_fields) {
|
||||
if (qs.sysparm_fields && typeof qs.sysparm_fields !== 'string') {
|
||||
qs.sysparm_fields = (qs.sysparm_fields as string[]).join(',');
|
||||
}
|
||||
|
||||
|
@ -771,7 +808,7 @@ export class ServiceNow implements INodeType {
|
|||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.message });
|
||||
returnData.push({ error: (error as JsonObject).message });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ export const tableRecordOperations: INodeProperties[] = [
|
|||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -71,7 +72,7 @@ export const tableRecordFields: INodeProperties[] = [
|
|||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Auto-map Input Data to Columns',
|
||||
name: 'Auto-Map Input Data to Columns',
|
||||
value: 'mapInput',
|
||||
description: 'Use when node input names match destination field names',
|
||||
},
|
||||
|
@ -117,7 +118,7 @@ export const tableRecordFields: INodeProperties[] = [
|
|||
},
|
||||
default: '',
|
||||
required: false,
|
||||
description: 'List of input properties to avoid sending, separated by commas. Leave empty to send all inputs',
|
||||
description: 'List of input properties to avoid sending, separated by commas. Leave empty to send all inputs.',
|
||||
},
|
||||
{
|
||||
displayName: 'Fields to Send',
|
||||
|
@ -208,7 +209,7 @@ export const tableRecordFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
|
@ -268,15 +269,16 @@ export const tableRecordFields: INodeProperties[] = [
|
|||
'tableName',
|
||||
],
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'A list of fields to return',
|
||||
hint: 'String of comma separated values or an array of strings can be set in an expression',
|
||||
},
|
||||
{
|
||||
displayName: 'Filter',
|
||||
name: 'sysparm_query',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Values',
|
||||
|
@ -380,8 +382,9 @@ export const tableRecordFields: INodeProperties[] = [
|
|||
'tableName',
|
||||
],
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'A list of fields to return',
|
||||
hint: 'String of comma separated values or an array of strings can be set in an expression',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Values',
|
||||
|
@ -455,7 +458,7 @@ export const tableRecordFields: INodeProperties[] = [
|
|||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Auto-map Input Data to Columns',
|
||||
name: 'Auto-Map Input Data to Columns',
|
||||
value: 'mapInput',
|
||||
description: 'Use when node input names match destination field names',
|
||||
},
|
||||
|
@ -501,7 +504,7 @@ export const tableRecordFields: INodeProperties[] = [
|
|||
},
|
||||
default: '',
|
||||
required: false,
|
||||
description: 'List of input properties to avoid sending, separated by commas. Leave empty to send all inputs',
|
||||
description: 'List of input properties to avoid sending, separated by commas. Leave empty to send all inputs.',
|
||||
},
|
||||
{
|
||||
displayName: 'Fields to Send',
|
||||
|
|
|
@ -7,6 +7,7 @@ export const userOperations: INodeProperties[] = [
|
|||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -84,7 +85,7 @@ export const userFields: INodeProperties[] = [
|
|||
displayName: 'Active',
|
||||
name: 'active',
|
||||
type: 'boolean',
|
||||
default: '',
|
||||
default: false,
|
||||
description: 'Whether to activate the user',
|
||||
},
|
||||
{
|
||||
|
@ -196,7 +197,7 @@ export const userFields: INodeProperties[] = [
|
|||
displayName: 'Password Needs Reset',
|
||||
name: 'password_needs_reset',
|
||||
type: 'boolean',
|
||||
default: '',
|
||||
default: false,
|
||||
description: 'Whether to require a password reset when the user logs in',
|
||||
},
|
||||
{
|
||||
|
@ -213,7 +214,7 @@ export const userFields: INodeProperties[] = [
|
|||
typeOptions: {
|
||||
loadOptionsMethod: 'getUserRoles',
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'Roles of the user',
|
||||
},
|
||||
{
|
||||
|
@ -242,6 +243,7 @@ export const userFields: INodeProperties[] = [
|
|||
name: 'user_name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
// nodelinter-ignore-next-line
|
||||
description: 'A username associated with the user (e.g. user_name.123)',
|
||||
},
|
||||
{
|
||||
|
@ -272,7 +274,7 @@ export const userFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
|
@ -332,15 +334,16 @@ export const userFields: INodeProperties[] = [
|
|||
'operation',
|
||||
],
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'A list of fields to return',
|
||||
hint: 'String of comma separated values or an array of strings can be set in an expression',
|
||||
},
|
||||
{
|
||||
displayName: 'Filter',
|
||||
name: 'sysparm_query',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Values',
|
||||
|
@ -491,8 +494,9 @@ export const userFields: INodeProperties[] = [
|
|||
'operation',
|
||||
],
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'A list of fields to return',
|
||||
hint: 'String of comma separated values or an array of strings can be set in an expression',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Values',
|
||||
|
@ -560,7 +564,7 @@ export const userFields: INodeProperties[] = [
|
|||
displayName: 'Active',
|
||||
name: 'active',
|
||||
type: 'boolean',
|
||||
default: '',
|
||||
default: false,
|
||||
description: 'Whether to activate the user',
|
||||
},
|
||||
{
|
||||
|
@ -672,7 +676,7 @@ export const userFields: INodeProperties[] = [
|
|||
displayName: 'Password Needs Reset',
|
||||
name: 'password_needs_reset',
|
||||
type: 'boolean',
|
||||
default: '',
|
||||
default: false,
|
||||
description: 'Whether to require a password reset when the user logs in',
|
||||
},
|
||||
{
|
||||
|
@ -689,7 +693,7 @@ export const userFields: INodeProperties[] = [
|
|||
typeOptions: {
|
||||
loadOptionsMethod: 'getUserRoles',
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'Roles of the user',
|
||||
},
|
||||
{
|
||||
|
@ -718,6 +722,7 @@ export const userFields: INodeProperties[] = [
|
|||
name: 'user_name',
|
||||
type: 'string',
|
||||
default: '',
|
||||
// nodelinter-ignore-next-line
|
||||
description: 'A username associated with the user (e.g. user_name.123)',
|
||||
},
|
||||
{
|
||||
|
|
|
@ -7,6 +7,7 @@ export const userGroupOperations: INodeProperties[] = [
|
|||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -44,7 +45,7 @@ export const userGroupFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
|
@ -101,15 +102,16 @@ export const userGroupFields: INodeProperties[] = [
|
|||
typeOptions: {
|
||||
loadOptionsMethod: 'getColumns',
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'A list of fields to return',
|
||||
hint: 'String of comma separated values or an array of strings can be set in an expression',
|
||||
},
|
||||
{
|
||||
displayName: 'Filter',
|
||||
name: 'sysparm_query',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Values',
|
||||
|
|
|
@ -7,6 +7,7 @@ export const userRoleOperations: INodeProperties[] = [
|
|||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
|
@ -44,7 +45,7 @@ export const userRoleFields: INodeProperties[] = [
|
|||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit',
|
||||
description: 'Whether to return all results or only up to a given limit',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
|
@ -101,15 +102,16 @@ export const userRoleFields: INodeProperties[] = [
|
|||
typeOptions: {
|
||||
loadOptionsMethod: 'getColumns',
|
||||
},
|
||||
default: '',
|
||||
default: [],
|
||||
description: 'A list of fields to return',
|
||||
hint: 'String of comma separated values or an array of strings can be set in an expression',
|
||||
},
|
||||
{
|
||||
displayName: 'Filter',
|
||||
name: 'sysparm_query',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>',
|
||||
description: 'An encoded query string used to filter the results. <a href="https://developer.servicenow.com/dev.do#!/learn/learning-plans/quebec/servicenow_application_developer/app_store_learnv2_rest_quebec_more_about_query_parameters">More info</a>.',
|
||||
},
|
||||
{
|
||||
displayName: 'Return Values',
|
||||
|
|
|
@ -46,6 +46,11 @@ export const activityOperations: INodeProperties[] = [
|
|||
value: 'getLaps',
|
||||
description: 'Get all activity laps',
|
||||
},
|
||||
{
|
||||
name: 'Get Streams',
|
||||
value: 'getStreams',
|
||||
description: 'Get activity streams',
|
||||
},
|
||||
{
|
||||
name: 'Get Zones',
|
||||
value: 'getZones',
|
||||
|
@ -316,6 +321,7 @@ export const activityFields: INodeProperties[] = [
|
|||
'getLaps',
|
||||
'getKudos',
|
||||
'getZones',
|
||||
'getStreams',
|
||||
],
|
||||
},
|
||||
},
|
||||
|
@ -369,7 +375,70 @@ export const activityFields: INodeProperties[] = [
|
|||
default: 50,
|
||||
description: 'How many results to return.',
|
||||
},
|
||||
|
||||
{
|
||||
displayName: 'Keys',
|
||||
name: 'keys',
|
||||
type: 'multiOptions',
|
||||
options: [
|
||||
{
|
||||
name: 'Altitude',
|
||||
value: 'altitude',
|
||||
},
|
||||
{
|
||||
name: 'Cadence',
|
||||
value: 'cadence',
|
||||
},
|
||||
{
|
||||
name: 'Distance',
|
||||
value: 'distance',
|
||||
},
|
||||
{
|
||||
name: 'Gradient',
|
||||
value: 'grade_smooth',
|
||||
},
|
||||
{
|
||||
name: 'Heartrate',
|
||||
value: 'heartrate',
|
||||
},
|
||||
{
|
||||
name: 'Latitude / Longitude',
|
||||
value: 'latlng',
|
||||
},
|
||||
{
|
||||
name: 'Moving',
|
||||
value: 'moving',
|
||||
},
|
||||
{
|
||||
name: 'Temperature',
|
||||
value: 'temp',
|
||||
},
|
||||
{
|
||||
name: 'Time',
|
||||
value: 'time',
|
||||
},
|
||||
{
|
||||
name: 'Velocity',
|
||||
value: 'velocity_smooth',
|
||||
},
|
||||
{
|
||||
name: 'Watts',
|
||||
value: 'watts',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'activity',
|
||||
],
|
||||
operation: [
|
||||
'getStreams',
|
||||
],
|
||||
},
|
||||
},
|
||||
required: true,
|
||||
default: [],
|
||||
description: 'Desired stream types to return',
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* activity:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
|
|
@ -129,6 +129,15 @@ export class Strava implements INodeType {
|
|||
responseData = responseData.splice(0, limit);
|
||||
}
|
||||
}
|
||||
//https://developers.strava.com/docs/reference/#api-Streams-getActivityStreams
|
||||
if (operation === 'getStreams') {
|
||||
const activityId = this.getNodeParameter('activityId', i) as string;
|
||||
const keys = this.getNodeParameter('keys', i) as string[];
|
||||
qs.keys = keys.toString();
|
||||
qs.key_by_type = true;
|
||||
|
||||
responseData = await stravaApiRequest.call(this, 'GET', `/activities/${activityId}/streams`, {}, qs);
|
||||
}
|
||||
//https://developers.mailerlite.com/reference#subscribers
|
||||
if (operation === 'getAll') {
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
|
|
|
@ -24,6 +24,7 @@ export async function supabaseApiRequest(this: IExecuteFunctions | IExecuteSingl
|
|||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
apikey: credentials.serviceRole,
|
||||
Authorization: 'Bearer ' + credentials.serviceRole,
|
||||
Prefer: 'return=representation',
|
||||
},
|
||||
method,
|
||||
|
@ -296,7 +297,7 @@ export const buildGetQuery = (obj: IDataObject, value: IDataObject) => {
|
|||
return Object.assign(obj, { [`${value.keyName}`]: `eq.${value.keyValue}` });
|
||||
};
|
||||
|
||||
export async function validateCrendentials(
|
||||
export async function validateCredentials(
|
||||
this: ICredentialTestFunctions,
|
||||
decryptedCredentials: ICredentialDataDecryptedObject): Promise<any> { // tslint:disable-line:no-any
|
||||
|
||||
|
@ -309,6 +310,7 @@ export async function validateCrendentials(
|
|||
const options: OptionsWithUri = {
|
||||
headers: {
|
||||
apikey: serviceRole,
|
||||
Authorization: 'Bearer ' + serviceRole,
|
||||
},
|
||||
method: 'GET',
|
||||
uri: `${credentials.host}/rest/v1/`,
|
||||
|
|
|
@ -228,7 +228,7 @@ export const rowFields: INodeProperties[] = [
|
|||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Select Conditions',
|
||||
name: 'primaryKey',
|
||||
name: 'filters',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
buildOrQuery,
|
||||
buildQuery,
|
||||
supabaseApiRequest,
|
||||
validateCrendentials,
|
||||
validateCredentials,
|
||||
} from './GenericFunctions';
|
||||
|
||||
import {
|
||||
|
@ -106,7 +106,7 @@ export class Supabase implements INodeType {
|
|||
credentialTest: {
|
||||
async supabaseApiCredentialTest(this: ICredentialTestFunctions, credential: ICredentialsDecrypted): Promise<INodeCredentialTestResult> {
|
||||
try {
|
||||
await validateCrendentials.call(this, credential.data as ICredentialDataDecryptedObject);
|
||||
await validateCredentials.call(this, credential.data as ICredentialDataDecryptedObject);
|
||||
} catch (error) {
|
||||
return {
|
||||
status: 'Error',
|
||||
|
|
|
@ -70,7 +70,7 @@ export async function wiseApiRequest(
|
|||
throw new NodeApiError(this.getNode(), error);
|
||||
}
|
||||
|
||||
if (response.statusCode === 200) {
|
||||
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||
return response.body;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const contactOperations: INodeProperties[] = [
|
||||
{
|
||||
|
@ -43,9 +43,9 @@ export const contactOperations: INodeProperties[] = [
|
|||
|
||||
export const contactFields: INodeProperties[] = [
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Organization ID',
|
||||
name: 'organizationId',
|
||||
|
@ -108,82 +108,82 @@ export const contactFields: INodeProperties[] = [
|
|||
default: '',
|
||||
description: 'A user defined account number',
|
||||
},
|
||||
// {
|
||||
// displayName: 'Addresses',
|
||||
// name: 'addressesUi',
|
||||
// type: 'fixedCollection',
|
||||
// typeOptions: {
|
||||
// multipleValues: true,
|
||||
// },
|
||||
// default: '',
|
||||
// placeholder: 'Add Address',
|
||||
// options: [
|
||||
// {
|
||||
// name: 'addressesValues',
|
||||
// displayName: 'Address',
|
||||
// values: [
|
||||
// {
|
||||
// displayName: 'Type',
|
||||
// name: 'type',
|
||||
// type: 'options',
|
||||
// options: [
|
||||
// {
|
||||
// name: 'PO Box',
|
||||
// value: 'POBOX',
|
||||
// },
|
||||
// {
|
||||
// name: 'Street',
|
||||
// value: 'STREET',
|
||||
// },
|
||||
// ],
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Line 1',
|
||||
// name: 'line1',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Line 2',
|
||||
// name: 'line2',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'City',
|
||||
// name: 'city',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Region',
|
||||
// name: 'region',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Postal Code',
|
||||
// name: 'postalCode',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Country',
|
||||
// name: 'country',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Attention To',
|
||||
// name: 'attentionTo',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
displayName: 'Addresses',
|
||||
name: 'addressesUi',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'Add Address',
|
||||
options: [
|
||||
{
|
||||
name: 'addressesValues',
|
||||
displayName: 'Address',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'PO Box',
|
||||
value: 'POBOX',
|
||||
},
|
||||
{
|
||||
name: 'Street',
|
||||
value: 'STREET',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Line 1',
|
||||
name: 'line1',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Line 2',
|
||||
name: 'line2',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'City',
|
||||
name: 'city',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Region',
|
||||
name: 'region',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Postal Code',
|
||||
name: 'postalCode',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Country',
|
||||
name: 'country',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Attention To',
|
||||
name: 'attentionTo',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Bank Account Details',
|
||||
name: 'bankAccountDetails',
|
||||
|
@ -250,66 +250,66 @@ export const contactFields: INodeProperties[] = [
|
|||
default: '',
|
||||
description: 'Last name of contact person (max length = 255)',
|
||||
},
|
||||
// {
|
||||
// displayName: 'Phones',
|
||||
// name: 'phonesUi',
|
||||
// type: 'fixedCollection',
|
||||
// typeOptions: {
|
||||
// multipleValues: true,
|
||||
// },
|
||||
// default: '',
|
||||
// placeholder: 'Add Phone',
|
||||
// options: [
|
||||
// {
|
||||
// name: 'phonesValues',
|
||||
// displayName: 'Phones',
|
||||
// values: [
|
||||
// {
|
||||
// displayName: 'Type',
|
||||
// name: 'type',
|
||||
// type: 'options',
|
||||
// options: [
|
||||
// {
|
||||
// name: 'Default',
|
||||
// value: 'DEFAULT',
|
||||
// },
|
||||
// {
|
||||
// name: 'DDI',
|
||||
// value: 'DDI',
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile',
|
||||
// value: 'MOBILE',
|
||||
// },
|
||||
// {
|
||||
// name: 'Fax',
|
||||
// value: 'FAX',
|
||||
// },
|
||||
// ],
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Number',
|
||||
// name: 'phoneNumber',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Area Code',
|
||||
// name: 'phoneAreaCode',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Country Code',
|
||||
// name: 'phoneCountryCode',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
displayName: 'Phones',
|
||||
name: 'phonesUi',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'Add Phone',
|
||||
options: [
|
||||
{
|
||||
name: 'phonesValues',
|
||||
displayName: 'Phones',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'phoneType',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Default',
|
||||
value: 'DEFAULT',
|
||||
},
|
||||
{
|
||||
name: 'DDI',
|
||||
value: 'DDI',
|
||||
},
|
||||
{
|
||||
name: 'Mobile',
|
||||
value: 'MOBILE',
|
||||
},
|
||||
{
|
||||
name: 'Fax',
|
||||
value: 'FAX',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Number',
|
||||
name: 'phoneNumber',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Area Code',
|
||||
name: 'phoneAreaCode',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Country Code',
|
||||
name: 'phoneCountryCode',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Purchase Default Account Code',
|
||||
name: 'purchasesDefaultAccountCode',
|
||||
|
@ -353,9 +353,9 @@ export const contactFields: INodeProperties[] = [
|
|||
},
|
||||
],
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Organization ID',
|
||||
name: 'organizationId',
|
||||
|
@ -393,9 +393,9 @@ export const contactFields: INodeProperties[] = [
|
|||
},
|
||||
required: true,
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Organization ID',
|
||||
name: 'organizationId',
|
||||
|
@ -519,9 +519,9 @@ export const contactFields: INodeProperties[] = [
|
|||
},
|
||||
],
|
||||
},
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:update */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* contact:update */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Organization ID',
|
||||
name: 'organizationId',
|
||||
|
@ -583,82 +583,82 @@ export const contactFields: INodeProperties[] = [
|
|||
default: '',
|
||||
description: 'A user defined account number',
|
||||
},
|
||||
// {
|
||||
// displayName: 'Addresses',
|
||||
// name: 'addressesUi',
|
||||
// type: 'fixedCollection',
|
||||
// typeOptions: {
|
||||
// multipleValues: true,
|
||||
// },
|
||||
// default: '',
|
||||
// placeholder: 'Add Address',
|
||||
// options: [
|
||||
// {
|
||||
// name: 'addressesValues',
|
||||
// displayName: 'Address',
|
||||
// values: [
|
||||
// {
|
||||
// displayName: 'Type',
|
||||
// name: 'type',
|
||||
// type: 'options',
|
||||
// options: [
|
||||
// {
|
||||
// name: 'PO Box',
|
||||
// value: 'POBOX',
|
||||
// },
|
||||
// {
|
||||
// name: 'Street',
|
||||
// value: 'STREET',
|
||||
// },
|
||||
// ],
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Line 1',
|
||||
// name: 'line1',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Line 2',
|
||||
// name: 'line2',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'City',
|
||||
// name: 'city',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Region',
|
||||
// name: 'region',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Postal Code',
|
||||
// name: 'postalCode',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Country',
|
||||
// name: 'country',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Attention To',
|
||||
// name: 'attentionTo',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
displayName: 'Addresses',
|
||||
name: 'addressesUi',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'Add Address',
|
||||
options: [
|
||||
{
|
||||
name: 'addressesValues',
|
||||
displayName: 'Address',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'PO Box',
|
||||
value: 'POBOX',
|
||||
},
|
||||
{
|
||||
name: 'Street',
|
||||
value: 'STREET',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Line 1',
|
||||
name: 'line1',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Line 2',
|
||||
name: 'line2',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'City',
|
||||
name: 'city',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Region',
|
||||
name: 'region',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Postal Code',
|
||||
name: 'postalCode',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Country',
|
||||
name: 'country',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Attention To',
|
||||
name: 'attentionTo',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Bank Account Details',
|
||||
name: 'bankAccountDetails',
|
||||
|
@ -732,66 +732,66 @@ export const contactFields: INodeProperties[] = [
|
|||
default: '',
|
||||
description: 'Full name of contact/organisation',
|
||||
},
|
||||
// {
|
||||
// displayName: 'Phones',
|
||||
// name: 'phonesUi',
|
||||
// type: 'fixedCollection',
|
||||
// typeOptions: {
|
||||
// multipleValues: true,
|
||||
// },
|
||||
// default: '',
|
||||
// placeholder: 'Add Phone',
|
||||
// options: [
|
||||
// {
|
||||
// name: 'phonesValues',
|
||||
// displayName: 'Phones',
|
||||
// values: [
|
||||
// {
|
||||
// displayName: 'Type',
|
||||
// name: 'type',
|
||||
// type: 'options',
|
||||
// options: [
|
||||
// {
|
||||
// name: 'Default',
|
||||
// value: 'DEFAULT',
|
||||
// },
|
||||
// {
|
||||
// name: 'DDI',
|
||||
// value: 'DDI',
|
||||
// },
|
||||
// {
|
||||
// name: 'Mobile',
|
||||
// value: 'MOBILE',
|
||||
// },
|
||||
// {
|
||||
// name: 'Fax',
|
||||
// value: 'FAX',
|
||||
// },
|
||||
// ],
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Number',
|
||||
// name: 'phoneNumber',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Area Code',
|
||||
// name: 'phoneAreaCode',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// {
|
||||
// displayName: 'Country Code',
|
||||
// name: 'phoneCountryCode',
|
||||
// type: 'string',
|
||||
// default: '',
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
displayName: 'Phones',
|
||||
name: 'phonesUi',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
default: '',
|
||||
placeholder: 'Add Phone',
|
||||
options: [
|
||||
{
|
||||
name: 'phonesValues',
|
||||
displayName: 'Phones',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'phoneType',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Default',
|
||||
value: 'DEFAULT',
|
||||
},
|
||||
{
|
||||
name: 'DDI',
|
||||
value: 'DDI',
|
||||
},
|
||||
{
|
||||
name: 'Mobile',
|
||||
value: 'MOBILE',
|
||||
},
|
||||
{
|
||||
name: 'Fax',
|
||||
value: 'FAX',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Number',
|
||||
name: 'phoneNumber',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Area Code',
|
||||
name: 'phoneAreaCode',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
{
|
||||
displayName: 'Country Code',
|
||||
name: 'phoneCountryCode',
|
||||
type: 'string',
|
||||
default: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Purchase Default Account Code',
|
||||
name: 'purchasesDefaultAccountCode',
|
||||
|
|
|
@ -9,7 +9,9 @@ import {
|
|||
} from 'n8n-core';
|
||||
|
||||
import {
|
||||
IDataObject, NodeApiError,
|
||||
IDataObject,
|
||||
JsonObject,
|
||||
NodeApiError,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export async function xeroApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
|
||||
|
@ -26,6 +28,7 @@ export async function xeroApiRequest(this: IExecuteFunctions | IExecuteSingleFun
|
|||
try {
|
||||
if (body.organizationId) {
|
||||
options.headers = { ...options.headers, 'Xero-tenant-id': body.organizationId };
|
||||
delete body.organizationId;
|
||||
}
|
||||
if (Object.keys(headers).length !== 0) {
|
||||
options.headers = Object.assign({}, options.headers, headers);
|
||||
|
@ -36,7 +39,7 @@ export async function xeroApiRequest(this: IExecuteFunctions | IExecuteSingleFun
|
|||
//@ts-ignore
|
||||
return await this.helpers.requestOAuth2.call(this, 'xeroOAuth2Api', options);
|
||||
} catch (error) {
|
||||
throw new NodeApiError(this.getNode(), error);
|
||||
throw new NodeApiError(this.getNode(), error as JsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
export interface IAddress {
|
||||
Type?: string;
|
||||
AddressType?: string;
|
||||
AddressLine1?: string;
|
||||
AddressLine2?: string;
|
||||
City?: string;
|
||||
|
@ -11,7 +11,7 @@ export interface IAddress {
|
|||
}
|
||||
|
||||
export interface IPhone {
|
||||
Type?: string;
|
||||
PhoneType?: string;
|
||||
PhoneNumber?: string;
|
||||
PhoneAreaCode?: string;
|
||||
PhoneCountryCode?: string;
|
||||
|
|
|
@ -9,6 +9,7 @@ import {
|
|||
INodePropertyOptions,
|
||||
INodeType,
|
||||
INodeTypeDescription,
|
||||
JsonObject,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
|
@ -32,9 +33,9 @@ import {
|
|||
} from './InvoiceInterface';
|
||||
|
||||
import {
|
||||
IAddress,
|
||||
IContact,
|
||||
// IPhone,
|
||||
// IAddress,
|
||||
IPhone,
|
||||
} from './IContactInterface';
|
||||
|
||||
export class Xero implements INodeType {
|
||||
|
@ -222,9 +223,9 @@ export class Xero implements INodeType {
|
|||
const lineItemsValues = ((this.getNodeParameter('lineItemsUi', i) as IDataObject).lineItemsValues as IDataObject[]);
|
||||
|
||||
const body: IInvoice = {
|
||||
organizationId,
|
||||
Type: type,
|
||||
Contact: { ContactID: contactId },
|
||||
organizationId,
|
||||
Type: type,
|
||||
Contact: { ContactID: contactId },
|
||||
};
|
||||
|
||||
if (lineItemsValues) {
|
||||
|
@ -311,7 +312,7 @@ export class Xero implements INodeType {
|
|||
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||
|
||||
const body: IInvoice = {
|
||||
organizationId,
|
||||
organizationId,
|
||||
};
|
||||
|
||||
if (updateFields.lineItemsUi) {
|
||||
|
@ -353,7 +354,7 @@ export class Xero implements INodeType {
|
|||
body.Type = updateFields.type as string;
|
||||
}
|
||||
if (updateFields.Contact) {
|
||||
body.Contact = { ContactID: updateFields.contactId as string };
|
||||
body.Contact = { ContactID: updateFields.contactId as string };
|
||||
}
|
||||
if (updateFields.brandingThemeId) {
|
||||
body.BrandingThemeID = updateFields.brandingThemeId as string;
|
||||
|
@ -438,11 +439,11 @@ export class Xero implements INodeType {
|
|||
const organizationId = this.getNodeParameter('organizationId', i) as string;
|
||||
const name = this.getNodeParameter('name', i) as string;
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
// const addressesUi = additionalFields.addressesUi as IDataObject;
|
||||
// const phonesUi = additionalFields.phonesUi as IDataObject;
|
||||
const addressesUi = additionalFields.addressesUi as IDataObject;
|
||||
const phonesUi = additionalFields.phonesUi as IDataObject;
|
||||
|
||||
const body: IContact = {
|
||||
Name: name,
|
||||
Name: name,
|
||||
};
|
||||
|
||||
if (additionalFields.accountNumber) {
|
||||
|
@ -497,41 +498,41 @@ export class Xero implements INodeType {
|
|||
body.xeroNetworkKey = additionalFields.xeroNetworkKey as string;
|
||||
}
|
||||
|
||||
// if (phonesUi) {
|
||||
// const phoneValues = phonesUi?.phonesValues as IDataObject[];
|
||||
// if (phoneValues) {
|
||||
// const phones: IPhone[] = [];
|
||||
// for (const phoneValue of phoneValues) {
|
||||
// const phone: IPhone = {};
|
||||
// phone.Type = phoneValue.type as string;
|
||||
// phone.PhoneNumber = phoneValue.PhoneNumber as string;
|
||||
// phone.PhoneAreaCode = phoneValue.phoneAreaCode as string;
|
||||
// phone.PhoneCountryCode = phoneValue.phoneCountryCode as string;
|
||||
// phones.push(phone);
|
||||
// }
|
||||
// body.Phones = phones;
|
||||
// }
|
||||
// }
|
||||
if (phonesUi) {
|
||||
const phoneValues = phonesUi?.phonesValues as IDataObject[];
|
||||
if (phoneValues) {
|
||||
const phones: IPhone[] = [];
|
||||
for (const phoneValue of phoneValues) {
|
||||
const phone: IPhone = {};
|
||||
phone.PhoneType = phoneValue.phoneType as string;
|
||||
phone.PhoneNumber = phoneValue.phoneNumber as string;
|
||||
phone.PhoneAreaCode = phoneValue.phoneAreaCode as string;
|
||||
phone.PhoneCountryCode = phoneValue.phoneCountryCode as string;
|
||||
phones.push(phone);
|
||||
}
|
||||
body.Phones = phones;
|
||||
}
|
||||
}
|
||||
|
||||
// if (addressesUi) {
|
||||
// const addressValues = addressesUi?.addressesValues as IDataObject[];
|
||||
// if (addressValues) {
|
||||
// const addresses: IAddress[] = [];
|
||||
// for (const addressValue of addressValues) {
|
||||
// const address: IAddress = {};
|
||||
// address.Type = addressValue.type as string;
|
||||
// address.AddressLine1 = addressValue.line1 as string;
|
||||
// address.AddressLine2 = addressValue.line2 as string;
|
||||
// address.City = addressValue.city as string;
|
||||
// address.Region = addressValue.region as string;
|
||||
// address.PostalCode = addressValue.postalCode as string;
|
||||
// address.Country = addressValue.country as string;
|
||||
// address.AttentionTo = addressValue.attentionTo as string;
|
||||
// addresses.push(address);
|
||||
// }
|
||||
// body.Addresses = addresses;
|
||||
// }
|
||||
// }
|
||||
if (addressesUi) {
|
||||
const addressValues = addressesUi?.addressesValues as IDataObject[];
|
||||
if (addressValues) {
|
||||
const addresses: IAddress[] = [];
|
||||
for (const addressValue of addressValues) {
|
||||
const address: IAddress = {};
|
||||
address.AddressType = addressValue.type as string;
|
||||
address.AddressLine1 = addressValue.line1 as string;
|
||||
address.AddressLine2 = addressValue.line2 as string;
|
||||
address.City = addressValue.city as string;
|
||||
address.Region = addressValue.region as string;
|
||||
address.PostalCode = addressValue.postalCode as string;
|
||||
address.Country = addressValue.country as string;
|
||||
address.AttentionTo = addressValue.attentionTo as string;
|
||||
addresses.push(address);
|
||||
}
|
||||
body.Addresses = addresses;
|
||||
}
|
||||
}
|
||||
|
||||
responseData = await xeroApiRequest.call(this, 'POST', '/Contacts', { organizationId, Contacts: [body] });
|
||||
responseData = responseData.Contacts;
|
||||
|
@ -569,8 +570,8 @@ export class Xero implements INodeType {
|
|||
const organizationId = this.getNodeParameter('organizationId', i) as string;
|
||||
const contactId = this.getNodeParameter('contactId', i) as string;
|
||||
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||
// const addressesUi = updateFields.addressesUi as IDataObject;
|
||||
// const phonesUi = updateFields.phonesUi as IDataObject;
|
||||
const addressesUi = updateFields.addressesUi as IDataObject;
|
||||
const phonesUi = updateFields.phonesUi as IDataObject;
|
||||
|
||||
const body: IContact = {};
|
||||
|
||||
|
@ -630,41 +631,41 @@ export class Xero implements INodeType {
|
|||
body.xeroNetworkKey = updateFields.xeroNetworkKey as string;
|
||||
}
|
||||
|
||||
// if (phonesUi) {
|
||||
// const phoneValues = phonesUi?.phonesValues as IDataObject[];
|
||||
// if (phoneValues) {
|
||||
// const phones: IPhone[] = [];
|
||||
// for (const phoneValue of phoneValues) {
|
||||
// const phone: IPhone = {};
|
||||
// phone.Type = phoneValue.type as string;
|
||||
// phone.PhoneNumber = phoneValue.PhoneNumber as string;
|
||||
// phone.PhoneAreaCode = phoneValue.phoneAreaCode as string;
|
||||
// phone.PhoneCountryCode = phoneValue.phoneCountryCode as string;
|
||||
// phones.push(phone);
|
||||
// }
|
||||
// body.Phones = phones;
|
||||
// }
|
||||
// }
|
||||
if (phonesUi) {
|
||||
const phoneValues = phonesUi?.phonesValues as IDataObject[];
|
||||
if (phoneValues) {
|
||||
const phones: IPhone[] = [];
|
||||
for (const phoneValue of phoneValues) {
|
||||
const phone: IPhone = {};
|
||||
phone.PhoneType = phoneValue.phoneType as string;
|
||||
phone.PhoneNumber = phoneValue.phoneNumber as string;
|
||||
phone.PhoneAreaCode = phoneValue.phoneAreaCode as string;
|
||||
phone.PhoneCountryCode = phoneValue.phoneCountryCode as string;
|
||||
phones.push(phone);
|
||||
}
|
||||
body.Phones = phones;
|
||||
}
|
||||
}
|
||||
|
||||
// if (addressesUi) {
|
||||
// const addressValues = addressesUi?.addressesValues as IDataObject[];
|
||||
// if (addressValues) {
|
||||
// const addresses: IAddress[] = [];
|
||||
// for (const addressValue of addressValues) {
|
||||
// const address: IAddress = {};
|
||||
// address.Type = addressValue.type as string;
|
||||
// address.AddressLine1 = addressValue.line1 as string;
|
||||
// address.AddressLine2 = addressValue.line2 as string;
|
||||
// address.City = addressValue.city as string;
|
||||
// address.Region = addressValue.region as string;
|
||||
// address.PostalCode = addressValue.postalCode as string;
|
||||
// address.Country = addressValue.country as string;
|
||||
// address.AttentionTo = addressValue.attentionTo as string;
|
||||
// addresses.push(address);
|
||||
// }
|
||||
// body.Addresses = addresses;
|
||||
// }
|
||||
// }
|
||||
if (addressesUi) {
|
||||
const addressValues = addressesUi?.addressesValues as IDataObject[];
|
||||
if (addressValues) {
|
||||
const addresses: IAddress[] = [];
|
||||
for (const addressValue of addressValues) {
|
||||
const address: IAddress = {};
|
||||
address.AddressType = addressValue.type as string;
|
||||
address.AddressLine1 = addressValue.line1 as string;
|
||||
address.AddressLine2 = addressValue.line2 as string;
|
||||
address.City = addressValue.city as string;
|
||||
address.Region = addressValue.region as string;
|
||||
address.PostalCode = addressValue.postalCode as string;
|
||||
address.Country = addressValue.country as string;
|
||||
address.AttentionTo = addressValue.attentionTo as string;
|
||||
addresses.push(address);
|
||||
}
|
||||
body.Addresses = addresses;
|
||||
}
|
||||
}
|
||||
|
||||
responseData = await xeroApiRequest.call(this, 'POST', `/Contacts/${contactId}`, { organizationId, Contacts: [body] });
|
||||
responseData = responseData.Contacts;
|
||||
|
@ -677,7 +678,7 @@ export class Xero implements INodeType {
|
|||
}
|
||||
} catch (error) {
|
||||
if (this.continueOnFail()) {
|
||||
returnData.push({ error: error.message });
|
||||
returnData.push({ error: (error as JsonObject).message });
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
|
|
|
@ -179,13 +179,21 @@ export const ticketFields: INodeProperties[] = [
|
|||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Open',
|
||||
value: 'open',
|
||||
name: 'Closed',
|
||||
value: 'closed',
|
||||
},
|
||||
{
|
||||
name: 'New',
|
||||
value: 'new',
|
||||
},
|
||||
{
|
||||
name: 'On-hold',
|
||||
value: 'hold',
|
||||
},
|
||||
{
|
||||
name: 'Open',
|
||||
value: 'open',
|
||||
},
|
||||
{
|
||||
name: 'Pending',
|
||||
value: 'pending',
|
||||
|
@ -194,10 +202,6 @@ export const ticketFields: INodeProperties[] = [
|
|||
name: 'Solved',
|
||||
value: 'solved',
|
||||
},
|
||||
{
|
||||
name: 'Closed',
|
||||
value: 'closed',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'The state of the ticket',
|
||||
|
@ -414,13 +418,21 @@ export const ticketFields: INodeProperties[] = [
|
|||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Open',
|
||||
value: 'open',
|
||||
name: 'Closed',
|
||||
value: 'closed',
|
||||
},
|
||||
{
|
||||
name: 'New',
|
||||
value: 'new',
|
||||
},
|
||||
{
|
||||
name: 'On-hold',
|
||||
value: 'hold',
|
||||
},
|
||||
{
|
||||
name: 'Open',
|
||||
value: 'open',
|
||||
},
|
||||
{
|
||||
name: 'Pending',
|
||||
value: 'pending',
|
||||
|
@ -429,10 +441,6 @@ export const ticketFields: INodeProperties[] = [
|
|||
name: 'Solved',
|
||||
value: 'solved',
|
||||
},
|
||||
{
|
||||
name: 'Closed',
|
||||
value: 'closed',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'The state of the ticket',
|
||||
|
@ -729,13 +737,21 @@ export const ticketFields: INodeProperties[] = [
|
|||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Open',
|
||||
value: 'open',
|
||||
name: 'Closed',
|
||||
value: 'closed',
|
||||
},
|
||||
{
|
||||
name: 'New',
|
||||
value: 'new',
|
||||
},
|
||||
{
|
||||
name: 'On-hold',
|
||||
value: 'hold',
|
||||
},
|
||||
{
|
||||
name: 'Open',
|
||||
value: 'open',
|
||||
},
|
||||
{
|
||||
name: 'Pending',
|
||||
value: 'pending',
|
||||
|
@ -744,10 +760,6 @@ export const ticketFields: INodeProperties[] = [
|
|||
name: 'Solved',
|
||||
value: 'solved',
|
||||
},
|
||||
{
|
||||
name: 'Closed',
|
||||
value: 'closed',
|
||||
},
|
||||
],
|
||||
default: '',
|
||||
description: 'The state of the ticket',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "n8n-nodes-base",
|
||||
"version": "0.167.0",
|
||||
"version": "0.169.0",
|
||||
"description": "Base nodes of n8n",
|
||||
"license": "SEE LICENSE IN LICENSE.md",
|
||||
"homepage": "https://n8n.io",
|
||||
|
@ -258,6 +258,7 @@
|
|||
"dist/credentials/SentryIoOAuth2Api.credentials.js",
|
||||
"dist/credentials/SentryIoServerApi.credentials.js",
|
||||
"dist/credentials/ServiceNowOAuth2Api.credentials.js",
|
||||
"dist/credentials/ServiceNowBasicApi.credentials.js",
|
||||
"dist/credentials/Sftp.credentials.js",
|
||||
"dist/credentials/ShopifyApi.credentials.js",
|
||||
"dist/credentials/Signl4Api.credentials.js",
|
||||
|
@ -712,7 +713,7 @@
|
|||
"@types/xml2js": "^0.4.3",
|
||||
"gulp": "^4.0.0",
|
||||
"jest": "^27.4.7",
|
||||
"n8n-workflow": "~0.92.0",
|
||||
"n8n-workflow": "~0.94.0",
|
||||
"nodelinter": "^0.1.9",
|
||||
"ts-jest": "^27.1.3",
|
||||
"tslint": "^6.1.2",
|
||||
|
@ -754,7 +755,7 @@
|
|||
"mqtt": "4.2.6",
|
||||
"mssql": "^6.2.0",
|
||||
"mysql2": "~2.3.0",
|
||||
"n8n-core": "~0.110.0",
|
||||
"n8n-core": "~0.112.0",
|
||||
"node-ssh": "^12.0.0",
|
||||
"nodemailer": "^6.5.0",
|
||||
"pdf-parse": "^1.1.1",
|
||||
|
@ -765,7 +766,7 @@
|
|||
"request": "^2.88.2",
|
||||
"rhea": "^1.0.11",
|
||||
"rss-parser": "^3.7.0",
|
||||
"simple-git": "^2.36.2",
|
||||
"simple-git": "^3.5.0",
|
||||
"snowflake-sdk": "^1.5.3",
|
||||
"ssh2-sftp-client": "^7.0.0",
|
||||
"tmp-promise": "^3.0.2",
|
||||
|
|
|
@ -12,7 +12,7 @@ The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensa
|
|||
|
||||
## Limitations
|
||||
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use
|
||||
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use.
|
||||
You may distribute the software or provide it to others only if you do so free of charge for non-commercial purposes.
|
||||
You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensor’s trademarks is subject to applicable law.
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue