n8n-nodes-pnpjs-sp 0.1.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/dist/credentials/PnPJsApi.credentials.d.ts +8 -0
- package/dist/credentials/PnPJsApi.credentials.js +89 -0
- package/dist/nodes/PnPJs/GenericFunctions.d.ts +14 -0
- package/dist/nodes/PnPJs/GenericFunctions.js +188 -0
- package/dist/nodes/PnPJs/PnPJs.node.d.ts +5 -0
- package/dist/nodes/PnPJs/PnPJs.node.js +1064 -0
- package/dist/nodes/PnPJs/handlers/advanced.d.ts +3 -0
- package/dist/nodes/PnPJs/handlers/advanced.js +876 -0
- package/dist/nodes/PnPJs/handlers/attachments.d.ts +3 -0
- package/dist/nodes/PnPJs/handlers/attachments.js +162 -0
- package/dist/nodes/PnPJs/handlers/comments.d.ts +3 -0
- package/dist/nodes/PnPJs/handlers/comments.js +117 -0
- package/dist/nodes/PnPJs/handlers/profilesSocial.d.ts +3 -0
- package/dist/nodes/PnPJs/handlers/profilesSocial.js +242 -0
- package/dist/nodes/PnPJs/handlers/security.d.ts +3 -0
- package/dist/nodes/PnPJs/handlers/security.js +198 -0
- package/dist/nodes/PnPJs/handlers/sharing.d.ts +3 -0
- package/dist/nodes/PnPJs/handlers/sharing.js +206 -0
- package/index.js +4 -0
- package/package.json +60 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class PnPJsApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
documentationUrl: string;
|
|
6
|
+
properties: INodeProperties[];
|
|
7
|
+
test: ICredentialTestRequest;
|
|
8
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PnPJsApi = void 0;
|
|
4
|
+
class PnPJsApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'pnpJsApi';
|
|
7
|
+
this.displayName = 'PnPJS SharePoint API';
|
|
8
|
+
this.documentationUrl = 'https://pnp.github.io/pnpjs/';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'SharePoint Site URL',
|
|
12
|
+
name: 'siteUrl',
|
|
13
|
+
type: 'string',
|
|
14
|
+
default: '',
|
|
15
|
+
placeholder: 'https://tenant.sharepoint.com/sites/mysite',
|
|
16
|
+
required: true,
|
|
17
|
+
description: 'Full URL of the SharePoint site, including the site path if applicable',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
displayName: 'Authentication Method',
|
|
21
|
+
name: 'authMethod',
|
|
22
|
+
type: 'options',
|
|
23
|
+
options: [
|
|
24
|
+
{
|
|
25
|
+
name: 'App-Only (Client Credentials)',
|
|
26
|
+
value: 'appOnly',
|
|
27
|
+
description: 'Uses Microsoft Entra ID app registration with client credentials flow',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Access Token',
|
|
31
|
+
value: 'accessToken',
|
|
32
|
+
description: 'Directly provide an OAuth 2.0 access token',
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
default: 'appOnly',
|
|
36
|
+
description: 'How to authenticate with SharePoint',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
displayName: 'Tenant ID',
|
|
40
|
+
name: 'tenantId',
|
|
41
|
+
type: 'string',
|
|
42
|
+
default: '',
|
|
43
|
+
required: true,
|
|
44
|
+
displayOptions: { show: { authMethod: ['appOnly'] } },
|
|
45
|
+
description: 'Microsoft Entra ID (Azure AD) tenant ID',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
displayName: 'Client ID',
|
|
49
|
+
name: 'clientId',
|
|
50
|
+
type: 'string',
|
|
51
|
+
default: '',
|
|
52
|
+
required: true,
|
|
53
|
+
displayOptions: { show: { authMethod: ['appOnly'] } },
|
|
54
|
+
description: 'Application (client) ID of the app registration',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
displayName: 'Client Secret',
|
|
58
|
+
name: 'clientSecret',
|
|
59
|
+
type: 'string',
|
|
60
|
+
typeOptions: { password: true },
|
|
61
|
+
default: '',
|
|
62
|
+
required: true,
|
|
63
|
+
displayOptions: { show: { authMethod: ['appOnly'] } },
|
|
64
|
+
description: 'Client secret value from the app registration',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
displayName: 'Access Token',
|
|
68
|
+
name: 'accessToken',
|
|
69
|
+
type: 'string',
|
|
70
|
+
typeOptions: { password: true },
|
|
71
|
+
default: '',
|
|
72
|
+
required: true,
|
|
73
|
+
displayOptions: { show: { authMethod: ['accessToken'] } },
|
|
74
|
+
description: 'OAuth 2.0 access token for SharePoint. Must have appropriate permissions.',
|
|
75
|
+
},
|
|
76
|
+
];
|
|
77
|
+
this.test = {
|
|
78
|
+
request: {
|
|
79
|
+
baseURL: '={{$credentials.siteUrl.replace(/\\/+$/, "")}}',
|
|
80
|
+
url: '/_api/web?$select=Title,Url',
|
|
81
|
+
method: 'GET',
|
|
82
|
+
headers: {
|
|
83
|
+
Accept: 'application/json',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.PnPJsApi = PnPJsApi;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { IExecuteFunctions, IHookFunctions, ILoadOptionsFunctions, IWebhookFunctions, IDataObject } from 'n8n-workflow';
|
|
2
|
+
export interface ISpCredentials {
|
|
3
|
+
siteUrl: string;
|
|
4
|
+
authMethod: string;
|
|
5
|
+
tenantId?: string;
|
|
6
|
+
clientId?: string;
|
|
7
|
+
clientSecret?: string;
|
|
8
|
+
accessToken?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function getSharePointAccessToken(this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions | IWebhookFunctions): Promise<string>;
|
|
11
|
+
export declare function getFormDigest(this: IExecuteFunctions | IHookFunctions | ILoadOptionsFunctions | IWebhookFunctions, siteUrl: string, accessToken: string): Promise<string>;
|
|
12
|
+
export declare function sharePointApiRequest(this: IExecuteFunctions | ILoadOptionsFunctions | IHookFunctions | IWebhookFunctions, method: string, endpoint: string, body?: IDataObject, qs?: IDataObject, binaryResponse?: boolean): Promise<any>;
|
|
13
|
+
export declare function sharePointApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, method: string, endpoint: string, body?: IDataObject, qs?: IDataObject): Promise<any[]>;
|
|
14
|
+
export declare function buildListEndpoint(listIdentifier: string): string;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getSharePointAccessToken = getSharePointAccessToken;
|
|
4
|
+
exports.getFormDigest = getFormDigest;
|
|
5
|
+
exports.sharePointApiRequest = sharePointApiRequest;
|
|
6
|
+
exports.sharePointApiRequestAllItems = sharePointApiRequestAllItems;
|
|
7
|
+
exports.buildListEndpoint = buildListEndpoint;
|
|
8
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
9
|
+
async function getSharePointAccessToken() {
|
|
10
|
+
const credentials = await this.getCredentials('pnpJsApi');
|
|
11
|
+
if (credentials.authMethod === 'accessToken') {
|
|
12
|
+
return credentials.accessToken;
|
|
13
|
+
}
|
|
14
|
+
const tenantId = credentials.tenantId;
|
|
15
|
+
const clientId = credentials.clientId;
|
|
16
|
+
const clientSecret = credentials.clientSecret;
|
|
17
|
+
const siteUrl = credentials.siteUrl;
|
|
18
|
+
const urlObj = new URL(siteUrl);
|
|
19
|
+
const hostname = urlObj.hostname;
|
|
20
|
+
const scope = `https://${hostname}/.default`;
|
|
21
|
+
const tokenEndpoint = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
|
|
22
|
+
const body = new URLSearchParams({
|
|
23
|
+
client_id: clientId,
|
|
24
|
+
client_secret: clientSecret,
|
|
25
|
+
scope,
|
|
26
|
+
grant_type: 'client_credentials',
|
|
27
|
+
});
|
|
28
|
+
const options = {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
url: tokenEndpoint,
|
|
31
|
+
body: body.toString(),
|
|
32
|
+
headers: {
|
|
33
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
34
|
+
},
|
|
35
|
+
json: false,
|
|
36
|
+
};
|
|
37
|
+
const response = await this.helpers.httpRequest(options);
|
|
38
|
+
let parsed;
|
|
39
|
+
try {
|
|
40
|
+
parsed = typeof response === 'string' ? JSON.parse(response) : response;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
|
|
44
|
+
message: 'Failed to acquire SharePoint access token',
|
|
45
|
+
description: 'Invalid response from token endpoint',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (parsed.error) {
|
|
49
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
|
|
50
|
+
message: `Token acquisition failed: ${parsed.error_description || parsed.error}`,
|
|
51
|
+
description: JSON.stringify(parsed),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return parsed.access_token;
|
|
55
|
+
}
|
|
56
|
+
async function getFormDigest(siteUrl, accessToken) {
|
|
57
|
+
const cleanUrl = siteUrl.replace(/\/+$/, '');
|
|
58
|
+
const options = {
|
|
59
|
+
method: 'POST',
|
|
60
|
+
url: `${cleanUrl}/_api/contextinfo`,
|
|
61
|
+
headers: {
|
|
62
|
+
Accept: 'application/json',
|
|
63
|
+
Authorization: `Bearer ${accessToken}`,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
const response = await this.helpers.httpRequest(options);
|
|
67
|
+
const respData = response;
|
|
68
|
+
const d = respData.d;
|
|
69
|
+
if (d) {
|
|
70
|
+
const ctxInfo = d.GetContextWebInformation;
|
|
71
|
+
return ctxInfo?.FormDigestValue || '';
|
|
72
|
+
}
|
|
73
|
+
return '';
|
|
74
|
+
}
|
|
75
|
+
async function sharePointApiRequest(method, endpoint, body, qs, binaryResponse = false) {
|
|
76
|
+
const credentials = await this.getCredentials('pnpJsApi');
|
|
77
|
+
const siteUrl = credentials.siteUrl.replace(/\/+$/, '');
|
|
78
|
+
const accessToken = await getSharePointAccessToken.call(this);
|
|
79
|
+
const url = `${siteUrl}/_api${endpoint}`;
|
|
80
|
+
const headers = {
|
|
81
|
+
Accept: 'application/json',
|
|
82
|
+
Authorization: `Bearer ${accessToken}`,
|
|
83
|
+
};
|
|
84
|
+
if (['POST', 'PUT', 'PATCH', 'MERGE'].includes(method) && body) {
|
|
85
|
+
const digest = await getFormDigest.call(this, siteUrl, accessToken);
|
|
86
|
+
headers['X-RequestDigest'] = digest;
|
|
87
|
+
headers['Content-Type'] = 'application/json;odata=verbose';
|
|
88
|
+
}
|
|
89
|
+
if (method === 'MERGE') {
|
|
90
|
+
headers['X-HTTP-Method'] = 'MERGE';
|
|
91
|
+
method = 'POST';
|
|
92
|
+
}
|
|
93
|
+
const options = {
|
|
94
|
+
method: method,
|
|
95
|
+
url,
|
|
96
|
+
headers,
|
|
97
|
+
json: !binaryResponse,
|
|
98
|
+
};
|
|
99
|
+
if (body) {
|
|
100
|
+
options.body = body;
|
|
101
|
+
}
|
|
102
|
+
if (qs) {
|
|
103
|
+
const queryString = Object.entries(qs)
|
|
104
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`)
|
|
105
|
+
.join('&');
|
|
106
|
+
options.url = `${url}?${queryString}`;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const response = await this.helpers.httpRequest(options);
|
|
110
|
+
if (binaryResponse) {
|
|
111
|
+
return response;
|
|
112
|
+
}
|
|
113
|
+
const data = response;
|
|
114
|
+
if (data && data.d) {
|
|
115
|
+
return data.d;
|
|
116
|
+
}
|
|
117
|
+
return data;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (error instanceof n8n_workflow_1.NodeApiError) {
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
let message = 'SharePoint API request failed';
|
|
124
|
+
let description = error.message || '';
|
|
125
|
+
if (error.response) {
|
|
126
|
+
const status = error.response.status;
|
|
127
|
+
const statusText = error.response.statusText || '';
|
|
128
|
+
message = `HTTP ${status}: ${statusText}`;
|
|
129
|
+
let responseData = error.response.data;
|
|
130
|
+
if (typeof responseData === 'string') {
|
|
131
|
+
try {
|
|
132
|
+
responseData = JSON.parse(responseData);
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
// ignore parse error, use raw string
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (responseData && responseData.error) {
|
|
139
|
+
const apiError = responseData.error;
|
|
140
|
+
const msg = apiError.message;
|
|
141
|
+
const msgValue = typeof msg === 'object' ? msg.value : msg;
|
|
142
|
+
description = `${msgValue || JSON.stringify(responseData)}`;
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
description = JSON.stringify(responseData);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else if (error.code === 'ECONNREFUSED') {
|
|
149
|
+
message = 'Connection refused - SharePoint server is unreachable';
|
|
150
|
+
description = `Unable to connect to ${url}`;
|
|
151
|
+
}
|
|
152
|
+
else if (error.code === 'ENOTFOUND') {
|
|
153
|
+
message = 'Host not found - Invalid URL or DNS issue';
|
|
154
|
+
description = `Cannot resolve hostname: ${siteUrl}`;
|
|
155
|
+
}
|
|
156
|
+
else if (error.code === 'ETIMEDOUT') {
|
|
157
|
+
message = 'Connection timeout - Server is not responding';
|
|
158
|
+
description = 'Request timed out';
|
|
159
|
+
}
|
|
160
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), {
|
|
161
|
+
message,
|
|
162
|
+
description,
|
|
163
|
+
httpCode: error.response?.status,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async function sharePointApiRequestAllItems(method, endpoint, body, qs) {
|
|
168
|
+
const allItems = [];
|
|
169
|
+
const response = await sharePointApiRequest.call(this, method, endpoint, body, qs);
|
|
170
|
+
const data = response;
|
|
171
|
+
const results = data.results || data;
|
|
172
|
+
if (Array.isArray(results)) {
|
|
173
|
+
allItems.push(...results);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
return [results];
|
|
177
|
+
}
|
|
178
|
+
return allItems;
|
|
179
|
+
}
|
|
180
|
+
function buildListEndpoint(listIdentifier) {
|
|
181
|
+
if (listIdentifier.startsWith("'") || listIdentifier.startsWith('guid')) {
|
|
182
|
+
return `/lists(${listIdentifier})`;
|
|
183
|
+
}
|
|
184
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(listIdentifier)) {
|
|
185
|
+
return `/lists(guid'${listIdentifier}')`;
|
|
186
|
+
}
|
|
187
|
+
return `/lists/getByTitle('${encodeURIComponent(listIdentifier).replace(/'/g, "''")}')`;
|
|
188
|
+
}
|