@blickwerk/n8n-nodes-microsoft-planner 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +21 -0
- package/README.md +157 -0
- package/dist/credentials/MicrosoftPlannerOAuth2Api.credentials.d.ts +10 -0
- package/dist/credentials/MicrosoftPlannerOAuth2Api.credentials.js +83 -0
- package/dist/nodes/MicrosoftPlanner/GenericFunctions.d.ts +4 -0
- package/dist/nodes/MicrosoftPlanner/GenericFunctions.js +47 -0
- package/dist/nodes/MicrosoftPlanner/MicrosoftPlanner.node.d.ts +5 -0
- package/dist/nodes/MicrosoftPlanner/MicrosoftPlanner.node.js +205 -0
- package/dist/nodes/MicrosoftPlanner/TaskDescription.d.ts +3 -0
- package/dist/nodes/MicrosoftPlanner/TaskDescription.js +321 -0
- package/dist/nodes/MicrosoftPlanner/planner.svg +13 -0
- package/package.json +58 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Blickwerk Media UG
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# n8n-nodes-microsoft-planner
|
|
2
|
+
|
|
3
|
+
This is an n8n community node that allows you to interact with Microsoft Planner in your n8n workflows.
|
|
4
|
+
|
|
5
|
+
[n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.
|
|
6
|
+
|
|
7
|
+
[Microsoft Planner](https://www.microsoft.com/en-us/microsoft-365/business/task-management-software) is a task management tool that helps teams organize, assign, and collaborate on tasks.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.
|
|
12
|
+
|
|
13
|
+
### Community Node
|
|
14
|
+
|
|
15
|
+
1. Go to **Settings > Community Nodes** in your n8n instance
|
|
16
|
+
2. Select **Install**
|
|
17
|
+
3. Enter `n8n-nodes-microsoft-planner` in **Enter npm package name**
|
|
18
|
+
4. Agree to the [risks](https://docs.n8n.io/integrations/community-nodes/risks/) of using community nodes
|
|
19
|
+
5. Select **Install**
|
|
20
|
+
|
|
21
|
+
After installing the node, you can use it like any other node in your workflows.
|
|
22
|
+
|
|
23
|
+
### Manual Installation
|
|
24
|
+
|
|
25
|
+
To install manually from source:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install n8n-nodes-microsoft-planner
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Prerequisites
|
|
32
|
+
|
|
33
|
+
You need to have:
|
|
34
|
+
- An active Microsoft 365 subscription
|
|
35
|
+
- A Microsoft Planner plan created
|
|
36
|
+
- Azure AD App Registration with the following API permissions:
|
|
37
|
+
- `Tasks.ReadWrite` - Read and write tasks
|
|
38
|
+
- `Group.ReadWrite.All` - Read and write all groups (required for Planner)
|
|
39
|
+
|
|
40
|
+
## Setting up Azure AD App
|
|
41
|
+
|
|
42
|
+
1. Go to [Azure Portal](https://portal.azure.com/)
|
|
43
|
+
2. Navigate to **Azure Active Directory > App registrations**
|
|
44
|
+
3. Click **New registration**
|
|
45
|
+
4. Give it a name (e.g., "n8n Microsoft Planner Integration")
|
|
46
|
+
5. Select **Accounts in this organizational directory only**
|
|
47
|
+
6. Add redirect URI: `https://your-n8n-instance.com/rest/oauth2-credential/callback`
|
|
48
|
+
7. Click **Register**
|
|
49
|
+
|
|
50
|
+
### Configure API Permissions
|
|
51
|
+
|
|
52
|
+
1. Go to **API permissions**
|
|
53
|
+
2. Click **Add a permission**
|
|
54
|
+
3. Select **Microsoft Graph**
|
|
55
|
+
4. Select **Delegated permissions**
|
|
56
|
+
5. Add the following permissions:
|
|
57
|
+
- `Tasks.ReadWrite`
|
|
58
|
+
- `Group.ReadWrite.All`
|
|
59
|
+
6. Click **Grant admin consent**
|
|
60
|
+
|
|
61
|
+
### Create Client Secret
|
|
62
|
+
|
|
63
|
+
1. Go to **Certificates & secrets**
|
|
64
|
+
2. Click **New client secret**
|
|
65
|
+
3. Add a description and select expiration
|
|
66
|
+
4. Copy the **Value** (you won't see it again!)
|
|
67
|
+
|
|
68
|
+
### Get Application Details
|
|
69
|
+
|
|
70
|
+
- **Client ID**: Found on the app overview page
|
|
71
|
+
- **Client Secret**: The value you copied in the previous step
|
|
72
|
+
|
|
73
|
+
## Credentials
|
|
74
|
+
|
|
75
|
+
Configure the Microsoft Planner OAuth2 API credentials in n8n:
|
|
76
|
+
|
|
77
|
+
1. In n8n, go to **Credentials > New**
|
|
78
|
+
2. Search for **Microsoft Planner OAuth2 API**
|
|
79
|
+
3. Enter your **Client ID** and **Client Secret**
|
|
80
|
+
4. Click **Connect my account**
|
|
81
|
+
5. Complete the OAuth flow
|
|
82
|
+
|
|
83
|
+
## Operations
|
|
84
|
+
|
|
85
|
+
### Task
|
|
86
|
+
|
|
87
|
+
- **Create** - Create a new task
|
|
88
|
+
- **Get** - Get a task by ID
|
|
89
|
+
- **Get Many** - Get multiple tasks from a plan or bucket
|
|
90
|
+
- **Update** - Update an existing task
|
|
91
|
+
- **Delete** - Delete a task
|
|
92
|
+
|
|
93
|
+
## Usage
|
|
94
|
+
|
|
95
|
+
### Creating a Task
|
|
96
|
+
|
|
97
|
+
To create a task, you need:
|
|
98
|
+
- **Plan ID**: The ID of the plan
|
|
99
|
+
- **Bucket ID**: The ID of the bucket within the plan
|
|
100
|
+
- **Title**: The task title
|
|
101
|
+
|
|
102
|
+
Optional fields:
|
|
103
|
+
- Description
|
|
104
|
+
- Priority (0-10, where 0 is most urgent)
|
|
105
|
+
- Due Date Time
|
|
106
|
+
- Start Date Time
|
|
107
|
+
- Percent Complete
|
|
108
|
+
|
|
109
|
+
### Getting Tasks
|
|
110
|
+
|
|
111
|
+
You can retrieve tasks by:
|
|
112
|
+
- **Task ID**: Get a specific task
|
|
113
|
+
- **Plan ID**: Get all tasks in a plan
|
|
114
|
+
- **Bucket ID**: Get all tasks in a bucket
|
|
115
|
+
|
|
116
|
+
### Updating a Task
|
|
117
|
+
|
|
118
|
+
To update a task, provide the Task ID and the fields you want to update.
|
|
119
|
+
|
|
120
|
+
### Finding Plan and Bucket IDs
|
|
121
|
+
|
|
122
|
+
You can find Plan IDs and Bucket IDs using the Microsoft Graph Explorer:
|
|
123
|
+
|
|
124
|
+
1. Go to [Microsoft Graph Explorer](https://developer.microsoft.com/en-us/graph/graph-explorer)
|
|
125
|
+
2. Sign in with your Microsoft account
|
|
126
|
+
3. Use these queries:
|
|
127
|
+
- Get all plans: `GET https://graph.microsoft.com/v1.0/me/planner/plans`
|
|
128
|
+
- Get buckets in a plan: `GET https://graph.microsoft.com/v1.0/planner/plans/{plan-id}/buckets`
|
|
129
|
+
|
|
130
|
+
## Compatibility
|
|
131
|
+
|
|
132
|
+
Tested with n8n version 1.0.0 and above.
|
|
133
|
+
|
|
134
|
+
## Resources
|
|
135
|
+
|
|
136
|
+
- [n8n community nodes documentation](https://docs.n8n.io/integrations/community-nodes/)
|
|
137
|
+
- [Microsoft Graph Planner API documentation](https://docs.microsoft.com/en-us/graph/api/resources/planner-overview)
|
|
138
|
+
- [Microsoft Graph Explorer](https://developer.microsoft.com/en-us/graph/graph-explorer)
|
|
139
|
+
|
|
140
|
+
## Version History
|
|
141
|
+
|
|
142
|
+
### 1.0.0
|
|
143
|
+
- Initial release
|
|
144
|
+
- Support for creating and retrieving Planner tasks
|
|
145
|
+
- OAuth2 authentication
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
[MIT](LICENSE.md)
|
|
150
|
+
|
|
151
|
+
## Author
|
|
152
|
+
|
|
153
|
+
Developed by **Blickwerk Media UG**
|
|
154
|
+
|
|
155
|
+
## Support
|
|
156
|
+
|
|
157
|
+
For issues, questions, or contributions, please visit the [GitHub repository](https://github.com/blickwerk/n8n-nodes-microsoft-planner).
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class MicrosoftPlannerOAuth2Api implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
extends: string[];
|
|
5
|
+
displayName: string;
|
|
6
|
+
documentationUrl: string;
|
|
7
|
+
properties: INodeProperties[];
|
|
8
|
+
authenticate: IAuthenticateGeneric;
|
|
9
|
+
test: ICredentialTestRequest;
|
|
10
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MicrosoftPlannerOAuth2Api = void 0;
|
|
4
|
+
class MicrosoftPlannerOAuth2Api {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'microsoftPlannerOAuth2Api';
|
|
7
|
+
this.extends = ['oAuth2Api'];
|
|
8
|
+
this.displayName = 'Microsoft Planner OAuth2 API';
|
|
9
|
+
this.documentationUrl = 'https://docs.microsoft.com/en-us/graph/auth-v2-user';
|
|
10
|
+
this.properties = [
|
|
11
|
+
{
|
|
12
|
+
displayName: 'Grant Type',
|
|
13
|
+
name: 'grantType',
|
|
14
|
+
type: 'hidden',
|
|
15
|
+
default: 'authorizationCode',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
displayName: 'Authorization URL',
|
|
19
|
+
name: 'authUrl',
|
|
20
|
+
type: 'hidden',
|
|
21
|
+
default: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
|
|
22
|
+
required: true,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
displayName: 'Access Token URL',
|
|
26
|
+
name: 'accessTokenUrl',
|
|
27
|
+
type: 'hidden',
|
|
28
|
+
default: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
|
|
29
|
+
required: true,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
displayName: 'Scope',
|
|
33
|
+
name: 'scope',
|
|
34
|
+
type: 'hidden',
|
|
35
|
+
default: 'openid offline_access Tasks.ReadWrite Group.ReadWrite.All',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
displayName: 'Auth URI Query Parameters',
|
|
39
|
+
name: 'authQueryParameters',
|
|
40
|
+
type: 'hidden',
|
|
41
|
+
default: 'response_mode=query',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
displayName: 'Authentication',
|
|
45
|
+
name: 'authentication',
|
|
46
|
+
type: 'hidden',
|
|
47
|
+
default: 'body',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
displayName: 'Client ID',
|
|
51
|
+
name: 'clientId',
|
|
52
|
+
type: 'string',
|
|
53
|
+
default: '',
|
|
54
|
+
required: true,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
displayName: 'Client Secret',
|
|
58
|
+
name: 'clientSecret',
|
|
59
|
+
type: 'string',
|
|
60
|
+
typeOptions: {
|
|
61
|
+
password: true,
|
|
62
|
+
},
|
|
63
|
+
default: '',
|
|
64
|
+
required: true,
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
this.authenticate = {
|
|
68
|
+
type: 'generic',
|
|
69
|
+
properties: {
|
|
70
|
+
headers: {
|
|
71
|
+
Authorization: '={{$credentials.tokenType}} {{$credentials.accessToken}}',
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
this.test = {
|
|
76
|
+
request: {
|
|
77
|
+
baseURL: 'https://graph.microsoft.com/v1.0',
|
|
78
|
+
url: '/me',
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.MicrosoftPlannerOAuth2Api = MicrosoftPlannerOAuth2Api;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { IDataObject, IExecuteFunctions, IHookFunctions, IHttpRequestMethods, ILoadOptionsFunctions } from 'n8n-workflow';
|
|
2
|
+
export declare function microsoftApiRequest(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, resource: string, body?: IDataObject, qs?: IDataObject, uri?: string, headers?: IDataObject): Promise<any>;
|
|
3
|
+
export declare function microsoftApiRequestAllItems(this: IHookFunctions | IExecuteFunctions | ILoadOptionsFunctions, propertyName: string, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, query?: IDataObject): Promise<any>;
|
|
4
|
+
export declare function cleanETag(eTag: string): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.microsoftApiRequest = microsoftApiRequest;
|
|
4
|
+
exports.microsoftApiRequestAllItems = microsoftApiRequestAllItems;
|
|
5
|
+
exports.cleanETag = cleanETag;
|
|
6
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
7
|
+
async function microsoftApiRequest(method, resource, body = {}, qs = {}, uri, headers = {}) {
|
|
8
|
+
const options = {
|
|
9
|
+
headers: {
|
|
10
|
+
'Content-Type': 'application/json',
|
|
11
|
+
},
|
|
12
|
+
method,
|
|
13
|
+
body,
|
|
14
|
+
qs,
|
|
15
|
+
url: uri || `https://graph.microsoft.com/v1.0${resource}`,
|
|
16
|
+
json: true,
|
|
17
|
+
};
|
|
18
|
+
try {
|
|
19
|
+
if (Object.keys(headers).length !== 0) {
|
|
20
|
+
options.headers = Object.assign({}, options.headers, headers);
|
|
21
|
+
}
|
|
22
|
+
if (Object.keys(body).length === 0) {
|
|
23
|
+
delete options.body;
|
|
24
|
+
}
|
|
25
|
+
return await this.helpers.requestOAuth2.call(this, 'microsoftPlannerOAuth2Api', options);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function microsoftApiRequestAllItems(propertyName, method, endpoint, body = {}, query = {}) {
|
|
32
|
+
const returnData = [];
|
|
33
|
+
let responseData;
|
|
34
|
+
let uri;
|
|
35
|
+
do {
|
|
36
|
+
responseData = await microsoftApiRequest.call(this, method, endpoint, body, query, uri);
|
|
37
|
+
uri = responseData['@odata.nextLink'];
|
|
38
|
+
returnData.push.apply(returnData, responseData[propertyName]);
|
|
39
|
+
} while (responseData['@odata.nextLink'] !== undefined);
|
|
40
|
+
return returnData;
|
|
41
|
+
}
|
|
42
|
+
function cleanETag(eTag) {
|
|
43
|
+
if (eTag.startsWith('W/"') || eTag.startsWith("W/'")) {
|
|
44
|
+
return eTag.substring(2);
|
|
45
|
+
}
|
|
46
|
+
return eTag;
|
|
47
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
export declare class MicrosoftPlanner implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MicrosoftPlanner = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const GenericFunctions_1 = require("./GenericFunctions");
|
|
6
|
+
const TaskDescription_1 = require("./TaskDescription");
|
|
7
|
+
class MicrosoftPlanner {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.description = {
|
|
10
|
+
displayName: 'Microsoft Planner',
|
|
11
|
+
name: 'microsoftPlanner',
|
|
12
|
+
icon: 'file:planner.svg',
|
|
13
|
+
group: ['transform'],
|
|
14
|
+
version: 1,
|
|
15
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
16
|
+
description: 'Create and retrieve tasks in Microsoft Planner',
|
|
17
|
+
defaults: {
|
|
18
|
+
name: 'Microsoft Planner',
|
|
19
|
+
},
|
|
20
|
+
inputs: ['main'],
|
|
21
|
+
outputs: ['main'],
|
|
22
|
+
credentials: [
|
|
23
|
+
{
|
|
24
|
+
name: 'microsoftPlannerOAuth2Api',
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
properties: [
|
|
29
|
+
{
|
|
30
|
+
displayName: 'Resource',
|
|
31
|
+
name: 'resource',
|
|
32
|
+
type: 'options',
|
|
33
|
+
noDataExpression: true,
|
|
34
|
+
options: [
|
|
35
|
+
{
|
|
36
|
+
name: 'Task',
|
|
37
|
+
value: 'task',
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
default: 'task',
|
|
41
|
+
},
|
|
42
|
+
...TaskDescription_1.taskOperations,
|
|
43
|
+
...TaskDescription_1.taskFields,
|
|
44
|
+
],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async execute() {
|
|
48
|
+
const items = this.getInputData();
|
|
49
|
+
const returnData = [];
|
|
50
|
+
const resource = this.getNodeParameter('resource', 0);
|
|
51
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
52
|
+
for (let i = 0; i < items.length; i++) {
|
|
53
|
+
try {
|
|
54
|
+
if (resource === 'task') {
|
|
55
|
+
// ----------------------------------
|
|
56
|
+
// task:create
|
|
57
|
+
// ----------------------------------
|
|
58
|
+
if (operation === 'create') {
|
|
59
|
+
const planId = this.getNodeParameter('planId', i);
|
|
60
|
+
const bucketId = this.getNodeParameter('bucketId', i);
|
|
61
|
+
const title = this.getNodeParameter('title', i);
|
|
62
|
+
const additionalFields = this.getNodeParameter('additionalFields', i);
|
|
63
|
+
const body = {
|
|
64
|
+
planId,
|
|
65
|
+
bucketId,
|
|
66
|
+
title,
|
|
67
|
+
};
|
|
68
|
+
if (additionalFields.priority !== undefined) {
|
|
69
|
+
body.priority = additionalFields.priority;
|
|
70
|
+
}
|
|
71
|
+
if (additionalFields.dueDateTime) {
|
|
72
|
+
body.dueDateTime = additionalFields.dueDateTime;
|
|
73
|
+
}
|
|
74
|
+
if (additionalFields.startDateTime) {
|
|
75
|
+
body.startDateTime = additionalFields.startDateTime;
|
|
76
|
+
}
|
|
77
|
+
if (additionalFields.percentComplete !== undefined) {
|
|
78
|
+
body.percentComplete = additionalFields.percentComplete;
|
|
79
|
+
}
|
|
80
|
+
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'POST', '/planner/tasks', body);
|
|
81
|
+
// Add description if provided
|
|
82
|
+
if (additionalFields.description) {
|
|
83
|
+
const details = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${responseData.id}/details`);
|
|
84
|
+
const eTag = (0, GenericFunctions_1.cleanETag)(details['@odata.etag']);
|
|
85
|
+
await GenericFunctions_1.microsoftApiRequest.call(this, 'PATCH', `/planner/tasks/${responseData.id}/details`, {
|
|
86
|
+
description: additionalFields.description,
|
|
87
|
+
}, {}, undefined, {
|
|
88
|
+
'If-Match': eTag,
|
|
89
|
+
});
|
|
90
|
+
responseData.description = additionalFields.description;
|
|
91
|
+
}
|
|
92
|
+
returnData.push(responseData);
|
|
93
|
+
}
|
|
94
|
+
// ----------------------------------
|
|
95
|
+
// task:get
|
|
96
|
+
// ----------------------------------
|
|
97
|
+
if (operation === 'get') {
|
|
98
|
+
const taskId = this.getNodeParameter('taskId', i);
|
|
99
|
+
const additionalFields = this.getNodeParameter('additionalFields', i);
|
|
100
|
+
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}`);
|
|
101
|
+
if (additionalFields.includeDetails) {
|
|
102
|
+
const details = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}/details`);
|
|
103
|
+
responseData.details = details;
|
|
104
|
+
}
|
|
105
|
+
returnData.push(responseData);
|
|
106
|
+
}
|
|
107
|
+
// ----------------------------------
|
|
108
|
+
// task:getAll
|
|
109
|
+
// ----------------------------------
|
|
110
|
+
if (operation === 'getAll') {
|
|
111
|
+
const returnAll = this.getNodeParameter('returnAll', i);
|
|
112
|
+
const filters = this.getNodeParameter('filters', i);
|
|
113
|
+
let endpoint = '';
|
|
114
|
+
if (filters.planId) {
|
|
115
|
+
endpoint = `/planner/plans/${filters.planId}/tasks`;
|
|
116
|
+
}
|
|
117
|
+
else if (filters.bucketId) {
|
|
118
|
+
endpoint = `/planner/buckets/${filters.bucketId}/tasks`;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'You must specify either a Plan ID or Bucket ID to retrieve tasks', { itemIndex: i });
|
|
122
|
+
}
|
|
123
|
+
if (returnAll) {
|
|
124
|
+
const responseData = await GenericFunctions_1.microsoftApiRequestAllItems.call(this, 'value', 'GET', endpoint);
|
|
125
|
+
returnData.push(...responseData);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const limit = this.getNodeParameter('limit', i);
|
|
129
|
+
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', endpoint, {}, {
|
|
130
|
+
$top: limit,
|
|
131
|
+
});
|
|
132
|
+
returnData.push(...responseData.value);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// ----------------------------------
|
|
136
|
+
// task:update
|
|
137
|
+
// ----------------------------------
|
|
138
|
+
if (operation === 'update') {
|
|
139
|
+
const taskId = this.getNodeParameter('taskId', i);
|
|
140
|
+
const updateFields = this.getNodeParameter('updateFields', i);
|
|
141
|
+
// Get current task to retrieve eTag
|
|
142
|
+
const currentTask = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}`);
|
|
143
|
+
const eTag = (0, GenericFunctions_1.cleanETag)(currentTask['@odata.etag']);
|
|
144
|
+
const body = {};
|
|
145
|
+
if (updateFields.title) {
|
|
146
|
+
body.title = updateFields.title;
|
|
147
|
+
}
|
|
148
|
+
if (updateFields.priority !== undefined) {
|
|
149
|
+
body.priority = updateFields.priority;
|
|
150
|
+
}
|
|
151
|
+
if (updateFields.dueDateTime) {
|
|
152
|
+
body.dueDateTime = updateFields.dueDateTime;
|
|
153
|
+
}
|
|
154
|
+
if (updateFields.startDateTime) {
|
|
155
|
+
body.startDateTime = updateFields.startDateTime;
|
|
156
|
+
}
|
|
157
|
+
if (updateFields.percentComplete !== undefined) {
|
|
158
|
+
body.percentComplete = updateFields.percentComplete;
|
|
159
|
+
}
|
|
160
|
+
if (updateFields.bucketId) {
|
|
161
|
+
body.bucketId = updateFields.bucketId;
|
|
162
|
+
}
|
|
163
|
+
const responseData = await GenericFunctions_1.microsoftApiRequest.call(this, 'PATCH', `/planner/tasks/${taskId}`, body, {}, undefined, {
|
|
164
|
+
'If-Match': eTag,
|
|
165
|
+
});
|
|
166
|
+
// Update description if provided
|
|
167
|
+
if (updateFields.description) {
|
|
168
|
+
const details = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}/details`);
|
|
169
|
+
const detailsETag = (0, GenericFunctions_1.cleanETag)(details['@odata.etag']);
|
|
170
|
+
await GenericFunctions_1.microsoftApiRequest.call(this, 'PATCH', `/planner/tasks/${taskId}/details`, {
|
|
171
|
+
description: updateFields.description,
|
|
172
|
+
}, {}, undefined, {
|
|
173
|
+
'If-Match': detailsETag,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
returnData.push(responseData);
|
|
177
|
+
}
|
|
178
|
+
// ----------------------------------
|
|
179
|
+
// task:delete
|
|
180
|
+
// ----------------------------------
|
|
181
|
+
if (operation === 'delete') {
|
|
182
|
+
const taskId = this.getNodeParameter('taskId', i);
|
|
183
|
+
// Get current task to retrieve eTag
|
|
184
|
+
const currentTask = await GenericFunctions_1.microsoftApiRequest.call(this, 'GET', `/planner/tasks/${taskId}`);
|
|
185
|
+
const eTag = (0, GenericFunctions_1.cleanETag)(currentTask['@odata.etag']);
|
|
186
|
+
await GenericFunctions_1.microsoftApiRequest.call(this, 'DELETE', `/planner/tasks/${taskId}`, {}, {}, undefined, {
|
|
187
|
+
'If-Match': eTag,
|
|
188
|
+
});
|
|
189
|
+
returnData.push({ success: true, taskId });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
if (this.continueOnFail()) {
|
|
195
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
196
|
+
returnData.push({ error: errorMessage });
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return [this.helpers.returnJsonArray(returnData)];
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
exports.MicrosoftPlanner = MicrosoftPlanner;
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.taskFields = exports.taskOperations = void 0;
|
|
4
|
+
exports.taskOperations = [
|
|
5
|
+
{
|
|
6
|
+
displayName: 'Operation',
|
|
7
|
+
name: 'operation',
|
|
8
|
+
type: 'options',
|
|
9
|
+
noDataExpression: true,
|
|
10
|
+
displayOptions: {
|
|
11
|
+
show: {
|
|
12
|
+
resource: ['task'],
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
options: [
|
|
16
|
+
{
|
|
17
|
+
name: 'Create',
|
|
18
|
+
value: 'create',
|
|
19
|
+
description: 'Create a new task',
|
|
20
|
+
action: 'Create a task',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: 'Get',
|
|
24
|
+
value: 'get',
|
|
25
|
+
description: 'Get a task',
|
|
26
|
+
action: 'Get a task',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'Get Many',
|
|
30
|
+
value: 'getAll',
|
|
31
|
+
description: 'Get many tasks',
|
|
32
|
+
action: 'Get many tasks',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'Update',
|
|
36
|
+
value: 'update',
|
|
37
|
+
description: 'Update a task',
|
|
38
|
+
action: 'Update a task',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'Delete',
|
|
42
|
+
value: 'delete',
|
|
43
|
+
description: 'Delete a task',
|
|
44
|
+
action: 'Delete a task',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
default: 'create',
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
exports.taskFields = [
|
|
51
|
+
// ----------------------------------
|
|
52
|
+
// task:create
|
|
53
|
+
// ----------------------------------
|
|
54
|
+
{
|
|
55
|
+
displayName: 'Plan ID',
|
|
56
|
+
name: 'planId',
|
|
57
|
+
type: 'string',
|
|
58
|
+
required: true,
|
|
59
|
+
displayOptions: {
|
|
60
|
+
show: {
|
|
61
|
+
resource: ['task'],
|
|
62
|
+
operation: ['create'],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
default: '',
|
|
66
|
+
description: 'The ID of the plan to which the task belongs',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
displayName: 'Bucket ID',
|
|
70
|
+
name: 'bucketId',
|
|
71
|
+
type: 'string',
|
|
72
|
+
required: true,
|
|
73
|
+
displayOptions: {
|
|
74
|
+
show: {
|
|
75
|
+
resource: ['task'],
|
|
76
|
+
operation: ['create'],
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
default: '',
|
|
80
|
+
description: 'The ID of the bucket to which the task belongs',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
displayName: 'Title',
|
|
84
|
+
name: 'title',
|
|
85
|
+
type: 'string',
|
|
86
|
+
required: true,
|
|
87
|
+
displayOptions: {
|
|
88
|
+
show: {
|
|
89
|
+
resource: ['task'],
|
|
90
|
+
operation: ['create'],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
default: '',
|
|
94
|
+
description: 'Title of the task',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
displayName: 'Additional Fields',
|
|
98
|
+
name: 'additionalFields',
|
|
99
|
+
type: 'collection',
|
|
100
|
+
placeholder: 'Add Field',
|
|
101
|
+
default: {},
|
|
102
|
+
displayOptions: {
|
|
103
|
+
show: {
|
|
104
|
+
resource: ['task'],
|
|
105
|
+
operation: ['create'],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
options: [
|
|
109
|
+
{
|
|
110
|
+
displayName: 'Description',
|
|
111
|
+
name: 'description',
|
|
112
|
+
type: 'string',
|
|
113
|
+
typeOptions: {
|
|
114
|
+
rows: 5,
|
|
115
|
+
},
|
|
116
|
+
default: '',
|
|
117
|
+
description: 'Description of the task',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
displayName: 'Priority',
|
|
121
|
+
name: 'priority',
|
|
122
|
+
type: 'number',
|
|
123
|
+
default: 5,
|
|
124
|
+
description: 'Priority of the task (0-10, where 0 is most urgent)',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
displayName: 'Due Date Time',
|
|
128
|
+
name: 'dueDateTime',
|
|
129
|
+
type: 'dateTime',
|
|
130
|
+
default: '',
|
|
131
|
+
description: 'Due date and time for the task',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
displayName: 'Start Date Time',
|
|
135
|
+
name: 'startDateTime',
|
|
136
|
+
type: 'dateTime',
|
|
137
|
+
default: '',
|
|
138
|
+
description: 'Start date and time for the task',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
displayName: 'Percent Complete',
|
|
142
|
+
name: 'percentComplete',
|
|
143
|
+
type: 'number',
|
|
144
|
+
default: 0,
|
|
145
|
+
description: 'Percentage of task completion (0-100)',
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
// ----------------------------------
|
|
150
|
+
// task:get
|
|
151
|
+
// ----------------------------------
|
|
152
|
+
{
|
|
153
|
+
displayName: 'Task ID',
|
|
154
|
+
name: 'taskId',
|
|
155
|
+
type: 'string',
|
|
156
|
+
required: true,
|
|
157
|
+
displayOptions: {
|
|
158
|
+
show: {
|
|
159
|
+
resource: ['task'],
|
|
160
|
+
operation: ['get', 'delete', 'update'],
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
default: '',
|
|
164
|
+
description: 'The ID of the task',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
displayName: 'Additional Fields',
|
|
168
|
+
name: 'additionalFields',
|
|
169
|
+
type: 'collection',
|
|
170
|
+
placeholder: 'Add Field',
|
|
171
|
+
default: {},
|
|
172
|
+
displayOptions: {
|
|
173
|
+
show: {
|
|
174
|
+
resource: ['task'],
|
|
175
|
+
operation: ['get'],
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
options: [
|
|
179
|
+
{
|
|
180
|
+
displayName: 'Include Details',
|
|
181
|
+
name: 'includeDetails',
|
|
182
|
+
type: 'boolean',
|
|
183
|
+
default: false,
|
|
184
|
+
description: 'Whether to include task details (description, checklist, etc.)',
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
// ----------------------------------
|
|
189
|
+
// task:getAll
|
|
190
|
+
// ----------------------------------
|
|
191
|
+
{
|
|
192
|
+
displayName: 'Return All',
|
|
193
|
+
name: 'returnAll',
|
|
194
|
+
type: 'boolean',
|
|
195
|
+
displayOptions: {
|
|
196
|
+
show: {
|
|
197
|
+
resource: ['task'],
|
|
198
|
+
operation: ['getAll'],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
default: false,
|
|
202
|
+
description: 'Whether to return all results or only up to a given limit',
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
displayName: 'Limit',
|
|
206
|
+
name: 'limit',
|
|
207
|
+
type: 'number',
|
|
208
|
+
displayOptions: {
|
|
209
|
+
show: {
|
|
210
|
+
resource: ['task'],
|
|
211
|
+
operation: ['getAll'],
|
|
212
|
+
returnAll: [false],
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
typeOptions: {
|
|
216
|
+
minValue: 1,
|
|
217
|
+
maxValue: 500,
|
|
218
|
+
},
|
|
219
|
+
default: 100,
|
|
220
|
+
description: 'Max number of results to return',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
displayName: 'Filters',
|
|
224
|
+
name: 'filters',
|
|
225
|
+
type: 'collection',
|
|
226
|
+
placeholder: 'Add Filter',
|
|
227
|
+
default: {},
|
|
228
|
+
displayOptions: {
|
|
229
|
+
show: {
|
|
230
|
+
resource: ['task'],
|
|
231
|
+
operation: ['getAll'],
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
options: [
|
|
235
|
+
{
|
|
236
|
+
displayName: 'Plan ID',
|
|
237
|
+
name: 'planId',
|
|
238
|
+
type: 'string',
|
|
239
|
+
default: '',
|
|
240
|
+
description: 'Filter tasks by plan ID',
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
displayName: 'Bucket ID',
|
|
244
|
+
name: 'bucketId',
|
|
245
|
+
type: 'string',
|
|
246
|
+
default: '',
|
|
247
|
+
description: 'Filter tasks by bucket ID',
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
// ----------------------------------
|
|
252
|
+
// task:update
|
|
253
|
+
// ----------------------------------
|
|
254
|
+
{
|
|
255
|
+
displayName: 'Update Fields',
|
|
256
|
+
name: 'updateFields',
|
|
257
|
+
type: 'collection',
|
|
258
|
+
placeholder: 'Add Field',
|
|
259
|
+
default: {},
|
|
260
|
+
displayOptions: {
|
|
261
|
+
show: {
|
|
262
|
+
resource: ['task'],
|
|
263
|
+
operation: ['update'],
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
options: [
|
|
267
|
+
{
|
|
268
|
+
displayName: 'Title',
|
|
269
|
+
name: 'title',
|
|
270
|
+
type: 'string',
|
|
271
|
+
default: '',
|
|
272
|
+
description: 'Title of the task',
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
displayName: 'Description',
|
|
276
|
+
name: 'description',
|
|
277
|
+
type: 'string',
|
|
278
|
+
typeOptions: {
|
|
279
|
+
rows: 5,
|
|
280
|
+
},
|
|
281
|
+
default: '',
|
|
282
|
+
description: 'Description of the task',
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
displayName: 'Priority',
|
|
286
|
+
name: 'priority',
|
|
287
|
+
type: 'number',
|
|
288
|
+
default: 5,
|
|
289
|
+
description: 'Priority of the task (0-10, where 0 is most urgent)',
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
displayName: 'Due Date Time',
|
|
293
|
+
name: 'dueDateTime',
|
|
294
|
+
type: 'dateTime',
|
|
295
|
+
default: '',
|
|
296
|
+
description: 'Due date and time for the task',
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
displayName: 'Start Date Time',
|
|
300
|
+
name: 'startDateTime',
|
|
301
|
+
type: 'dateTime',
|
|
302
|
+
default: '',
|
|
303
|
+
description: 'Start date and time for the task',
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
displayName: 'Percent Complete',
|
|
307
|
+
name: 'percentComplete',
|
|
308
|
+
type: 'number',
|
|
309
|
+
default: 0,
|
|
310
|
+
description: 'Percentage of task completion (0-100)',
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
displayName: 'Bucket ID',
|
|
314
|
+
name: 'bucketId',
|
|
315
|
+
type: 'string',
|
|
316
|
+
default: '',
|
|
317
|
+
description: 'Move task to a different bucket',
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
},
|
|
321
|
+
];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="a" x1="16" y1="2.5" x2="16" y2="29.5" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop offset="0" stop-color="#31ad5f"/>
|
|
5
|
+
<stop offset="1" stop-color="#1d6f42"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
</defs>
|
|
8
|
+
<rect x="2" y="2.5" width="28" height="27" rx="1.5" fill="url(#a)"/>
|
|
9
|
+
<path d="M7 9h7v2H7zm0 4h10v2H7zm0 4h8v2H7z" fill="#fff"/>
|
|
10
|
+
<circle cx="22" cy="10" r="2.5" fill="#fff"/>
|
|
11
|
+
<circle cx="22" cy="15" r="2.5" fill="#fff"/>
|
|
12
|
+
<circle cx="22" cy="20" r="2.5" fill="#fff"/>
|
|
13
|
+
</svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@blickwerk/n8n-nodes-microsoft-planner",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "n8n node for Microsoft Planner - Create and retrieve tasks",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"microsoft",
|
|
9
|
+
"planner",
|
|
10
|
+
"tasks"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"homepage": "https://github.com/blickwerk/n8n-nodes-microsoft-planner",
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "Blickwerk Media UG",
|
|
16
|
+
"email": "info@blickwerk-media.de"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/blickwerk/n8n-nodes-microsoft-planner.git"
|
|
21
|
+
},
|
|
22
|
+
"main": "index.js",
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc && gulp build:icons",
|
|
25
|
+
"dev": "tsc --watch",
|
|
26
|
+
"format": "prettier nodes credentials --write",
|
|
27
|
+
"lint": "eslint nodes credentials package.json",
|
|
28
|
+
"lintfix": "eslint nodes credentials package.json --fix",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist"
|
|
33
|
+
],
|
|
34
|
+
"n8n": {
|
|
35
|
+
"n8nNodesApiVersion": 1,
|
|
36
|
+
"credentials": [
|
|
37
|
+
"dist/credentials/MicrosoftPlannerOAuth2Api.credentials.js"
|
|
38
|
+
],
|
|
39
|
+
"nodes": [
|
|
40
|
+
"dist/nodes/MicrosoftPlanner/MicrosoftPlanner.node.js"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@typescript-eslint/parser": "^5.59.0",
|
|
45
|
+
"eslint": "^8.40.0",
|
|
46
|
+
"eslint-plugin-n8n-nodes-base": "^1.12.0",
|
|
47
|
+
"gulp": "^4.0.2",
|
|
48
|
+
"n8n-workflow": "^1.0.0",
|
|
49
|
+
"prettier": "^2.8.8",
|
|
50
|
+
"typescript": "^5.0.4"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"n8n-core": "^1.0.0"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"n8n-workflow": "^1.0.0"
|
|
57
|
+
}
|
|
58
|
+
}
|