n8n-nodes-sharepointexcel 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/README.md +148 -0
- package/dist/credentials/SharePointOAuth2Api.credentials.d.ts +8 -0
- package/dist/credentials/SharePointOAuth2Api.credentials.js +50 -0
- package/dist/nodes/SharePointExcel/SharePointExcel.node.d.ts +5 -0
- package/dist/nodes/SharePointExcel/SharePointExcel.node.js +426 -0
- package/dist/nodes/SharePointExcel/sharepoint.svg +34 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# N8N SharePoint Excel Node
|
|
2
|
+
|
|
3
|
+
An N8N community node that allows you to read Excel tables from SharePoint and perform table lookups.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Get Table**: Retrieve all data from a named Excel table in SharePoint
|
|
8
|
+
- **Lookup**: Search for values in a table column and return matching rows
|
|
9
|
+
- **Get Table Names**: List all table names in an Excel file
|
|
10
|
+
- **Multiple Match Types**: Exact match, contains, starts with, ends with
|
|
11
|
+
- **Flexible Output**: Return all matches or just the first one
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
### Via NPM (Recommended)
|
|
16
|
+
|
|
17
|
+
N8N will automatically detect and install this node when you use it in a workflow, or you can install it manually:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install n8n-nodes-sharepointexcel
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Manual Installation
|
|
24
|
+
|
|
25
|
+
1. Install dependencies:
|
|
26
|
+
```bash
|
|
27
|
+
npm install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
2. Build the node:
|
|
31
|
+
```bash
|
|
32
|
+
npm run build
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
3. Copy the `dist` folder to your N8N custom nodes directory:
|
|
36
|
+
- For Docker: `/home/node/.n8n/custom/`
|
|
37
|
+
- For npm: `~/.n8n/custom/`
|
|
38
|
+
|
|
39
|
+
## Setup
|
|
40
|
+
|
|
41
|
+
### Azure AD App Registration
|
|
42
|
+
|
|
43
|
+
1. Go to [Azure Portal](https://portal.azure.com) → Azure Active Directory → App registrations
|
|
44
|
+
2. Create a new registration
|
|
45
|
+
3. Note your **Application (client) ID** and **Directory (tenant) ID**
|
|
46
|
+
4. Go to "Certificates & secrets" → Create a new client secret
|
|
47
|
+
5. Go to "API permissions" → Add permissions:
|
|
48
|
+
- Microsoft Graph → Delegated permissions:
|
|
49
|
+
- `Files.Read.All`
|
|
50
|
+
- `Sites.Read.All`
|
|
51
|
+
- Click "Grant admin consent"
|
|
52
|
+
|
|
53
|
+
### N8N Credentials Setup
|
|
54
|
+
|
|
55
|
+
1. In N8N, go to Credentials → Add Credential
|
|
56
|
+
2. Select "SharePoint OAuth2 API"
|
|
57
|
+
3. Enter your:
|
|
58
|
+
- **Client ID**: Your Azure AD Application ID
|
|
59
|
+
- **Client Secret**: Your Azure AD Client Secret
|
|
60
|
+
- **Tenant ID**: Your Azure AD Tenant ID
|
|
61
|
+
4. Complete the OAuth2 flow to authorize access
|
|
62
|
+
|
|
63
|
+
## Usage
|
|
64
|
+
|
|
65
|
+
### Get Table Names
|
|
66
|
+
|
|
67
|
+
1. Add the "SharePoint Excel" node to your workflow
|
|
68
|
+
2. Select operation: "Get Table Names"
|
|
69
|
+
3. Enter:
|
|
70
|
+
- **SharePoint Site URL**: `https://yourtenant.sharepoint.com/sites/yoursite`
|
|
71
|
+
- **File Path**: `/Shared Documents/MyFile.xlsx`
|
|
72
|
+
4. Execute to get a list of all table names
|
|
73
|
+
|
|
74
|
+
### Get Table Data
|
|
75
|
+
|
|
76
|
+
1. Select operation: "Get Table"
|
|
77
|
+
2. Enter:
|
|
78
|
+
- **SharePoint Site URL**: Your SharePoint site URL
|
|
79
|
+
- **File Path**: Path to your Excel file
|
|
80
|
+
- **Table Name**: Name of the Excel table (e.g., "Table1")
|
|
81
|
+
3. Execute to get all rows from the table
|
|
82
|
+
|
|
83
|
+
### Table Lookup
|
|
84
|
+
|
|
85
|
+
1. Select operation: "Lookup"
|
|
86
|
+
2. Enter:
|
|
87
|
+
- **SharePoint Site URL**: Your SharePoint site URL
|
|
88
|
+
- **File Path**: Path to your Excel file
|
|
89
|
+
- **Table Name**: Name of the Excel table
|
|
90
|
+
- **Lookup Column**: Column name to search in
|
|
91
|
+
- **Lookup Value**: Value to search for
|
|
92
|
+
- **Match Type**: How to match (exact, contains, startsWith, endsWith)
|
|
93
|
+
- **Return All Matches**: Whether to return all matches or just the first
|
|
94
|
+
3. Execute to get matching rows
|
|
95
|
+
|
|
96
|
+
## Example Workflow
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
Trigger → SharePoint Excel (Get Table Names) →
|
|
100
|
+
SharePoint Excel (Lookup) → Process Results
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## File Path Format
|
|
104
|
+
|
|
105
|
+
The file path should be relative to the SharePoint site root:
|
|
106
|
+
- `/Shared Documents/MyFile.xlsx`
|
|
107
|
+
- `/Documents/Reports/Data.xlsx`
|
|
108
|
+
- `/SiteAssets/Data/Table.xlsx`
|
|
109
|
+
|
|
110
|
+
## Excel Table Requirements
|
|
111
|
+
|
|
112
|
+
- The Excel file must contain named tables (created via Insert → Table in Excel)
|
|
113
|
+
- Table names are case-sensitive
|
|
114
|
+
- Column names are taken from the table header row
|
|
115
|
+
|
|
116
|
+
## Troubleshooting
|
|
117
|
+
|
|
118
|
+
### "Table not found" error
|
|
119
|
+
- Ensure the table name matches exactly (case-sensitive)
|
|
120
|
+
- Verify the table exists in the Excel file
|
|
121
|
+
- Use "Get Table Names" operation to list available tables
|
|
122
|
+
|
|
123
|
+
### Authentication errors
|
|
124
|
+
- Verify your Azure AD app has the correct permissions
|
|
125
|
+
- Ensure admin consent has been granted
|
|
126
|
+
- Check that the OAuth2 flow completed successfully
|
|
127
|
+
|
|
128
|
+
### File not found
|
|
129
|
+
- Verify the file path is correct
|
|
130
|
+
- Ensure the path starts with `/`
|
|
131
|
+
- Check that the file exists in SharePoint
|
|
132
|
+
|
|
133
|
+
## Development
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Build
|
|
137
|
+
npm run build
|
|
138
|
+
|
|
139
|
+
# Watch mode
|
|
140
|
+
npm run dev
|
|
141
|
+
|
|
142
|
+
# Lint
|
|
143
|
+
npm run lint
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SharePointOAuth2Api = void 0;
|
|
4
|
+
class SharePointOAuth2Api {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = "sharePointOAuth2Api";
|
|
7
|
+
this.displayName = "SharePoint OAuth2 API";
|
|
8
|
+
this.documentationUrl = "https://docs.microsoft.com/en-us/graph/auth/";
|
|
9
|
+
this.extends = ["oAuth2Api"];
|
|
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
|
+
},
|
|
23
|
+
{
|
|
24
|
+
displayName: "Access Token URL",
|
|
25
|
+
name: "accessTokenUrl",
|
|
26
|
+
type: "hidden",
|
|
27
|
+
default: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
displayName: "Scope",
|
|
31
|
+
name: "scope",
|
|
32
|
+
type: "hidden",
|
|
33
|
+
default: "https://graph.microsoft.com/.default",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
displayName: "Auth URI Query Parameters",
|
|
37
|
+
name: "authQueryParameters",
|
|
38
|
+
type: "hidden",
|
|
39
|
+
default: "",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
displayName: "Authentication",
|
|
43
|
+
name: "authentication",
|
|
44
|
+
type: "hidden",
|
|
45
|
+
default: "body",
|
|
46
|
+
},
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.SharePointOAuth2Api = SharePointOAuth2Api;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from "n8n-workflow";
|
|
2
|
+
export declare class SharePointExcel implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.SharePointExcel = void 0;
|
|
37
|
+
const microsoft_graph_client_1 = require("@microsoft/microsoft-graph-client");
|
|
38
|
+
const ExcelJS = __importStar(require("exceljs"));
|
|
39
|
+
class SharePointExcel {
|
|
40
|
+
constructor() {
|
|
41
|
+
this.description = {
|
|
42
|
+
displayName: "SharePoint Excel",
|
|
43
|
+
name: "sharePointExcel",
|
|
44
|
+
icon: "file:sharepoint.svg",
|
|
45
|
+
group: ["transform"],
|
|
46
|
+
version: 1,
|
|
47
|
+
subtitle: '={{$parameter["operation"]}}',
|
|
48
|
+
description: "Read Excel tables from SharePoint and perform table lookups",
|
|
49
|
+
defaults: {
|
|
50
|
+
name: "SharePoint Excel",
|
|
51
|
+
},
|
|
52
|
+
inputs: ["main"],
|
|
53
|
+
outputs: ["main"],
|
|
54
|
+
credentials: [
|
|
55
|
+
{
|
|
56
|
+
name: "sharePointOAuth2Api",
|
|
57
|
+
required: true,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
properties: [
|
|
61
|
+
{
|
|
62
|
+
displayName: "Operation",
|
|
63
|
+
name: "operation",
|
|
64
|
+
type: "options",
|
|
65
|
+
noDataExpression: true,
|
|
66
|
+
options: [
|
|
67
|
+
{
|
|
68
|
+
name: "Get Table",
|
|
69
|
+
value: "getTable",
|
|
70
|
+
description: "Get all data from an Excel table",
|
|
71
|
+
action: "Get all data from a table",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: "Lookup",
|
|
75
|
+
value: "lookup",
|
|
76
|
+
description: "Lookup a value in a table and return matching row(s)",
|
|
77
|
+
action: "Lookup a value in a table",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "Get Table Names",
|
|
81
|
+
value: "getTableNames",
|
|
82
|
+
description: "Get list of table names in the Excel file",
|
|
83
|
+
action: "Get list of table names",
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
default: "getTable",
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
displayName: "SharePoint Site URL",
|
|
90
|
+
name: "siteUrl",
|
|
91
|
+
type: "string",
|
|
92
|
+
required: true,
|
|
93
|
+
displayOptions: {
|
|
94
|
+
show: {
|
|
95
|
+
operation: ["getTable", "lookup", "getTableNames"],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
default: "",
|
|
99
|
+
placeholder: "https://yourtenant.sharepoint.com/sites/yoursite",
|
|
100
|
+
description: "The SharePoint site URL where the Excel file is located",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
displayName: "File Path",
|
|
104
|
+
name: "filePath",
|
|
105
|
+
type: "string",
|
|
106
|
+
required: true,
|
|
107
|
+
displayOptions: {
|
|
108
|
+
show: {
|
|
109
|
+
operation: ["getTable", "lookup", "getTableNames"],
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
default: "",
|
|
113
|
+
placeholder: "/Shared Documents/MyFile.xlsx",
|
|
114
|
+
description: "The path to the Excel file in SharePoint",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
displayName: "Table Name",
|
|
118
|
+
name: "tableName",
|
|
119
|
+
type: "string",
|
|
120
|
+
required: true,
|
|
121
|
+
displayOptions: {
|
|
122
|
+
show: {
|
|
123
|
+
operation: ["getTable", "lookup"],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
default: "",
|
|
127
|
+
description: "The name of the Excel table to work with",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
displayName: "Lookup Column",
|
|
131
|
+
name: "lookupColumn",
|
|
132
|
+
type: "string",
|
|
133
|
+
required: true,
|
|
134
|
+
displayOptions: {
|
|
135
|
+
show: {
|
|
136
|
+
operation: ["lookup"],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
default: "",
|
|
140
|
+
description: "The column name to search in",
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
displayName: "Lookup Value",
|
|
144
|
+
name: "lookupValue",
|
|
145
|
+
type: "string",
|
|
146
|
+
required: true,
|
|
147
|
+
displayOptions: {
|
|
148
|
+
show: {
|
|
149
|
+
operation: ["lookup"],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
default: "",
|
|
153
|
+
description: "The value to search for",
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
displayName: "Match Type",
|
|
157
|
+
name: "matchType",
|
|
158
|
+
type: "options",
|
|
159
|
+
displayOptions: {
|
|
160
|
+
show: {
|
|
161
|
+
operation: ["lookup"],
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
options: [
|
|
165
|
+
{
|
|
166
|
+
name: "Exact Match",
|
|
167
|
+
value: "exact",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: "Contains",
|
|
171
|
+
value: "contains",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "Starts With",
|
|
175
|
+
value: "startsWith",
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "Ends With",
|
|
179
|
+
value: "endsWith",
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
default: "exact",
|
|
183
|
+
description: "How to match the lookup value",
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
displayName: "Return All Matches",
|
|
187
|
+
name: "returnAll",
|
|
188
|
+
type: "boolean",
|
|
189
|
+
displayOptions: {
|
|
190
|
+
show: {
|
|
191
|
+
operation: ["lookup"],
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
default: false,
|
|
195
|
+
description: "Whether to return all matching rows or just the first one",
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
async execute() {
|
|
201
|
+
const items = this.getInputData();
|
|
202
|
+
const returnData = [];
|
|
203
|
+
const nodeInstance = this;
|
|
204
|
+
const operation = this.getNodeParameter("operation", 0);
|
|
205
|
+
// Get credentials
|
|
206
|
+
const credentials = await this.getCredentials("sharePointOAuth2Api");
|
|
207
|
+
// Initialize Microsoft Graph client with access token
|
|
208
|
+
const accessToken = credentials.accessToken;
|
|
209
|
+
const client = microsoft_graph_client_1.Client.init({
|
|
210
|
+
authProvider: (done) => {
|
|
211
|
+
done(null, accessToken);
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
// Create helper instance to access private methods
|
|
215
|
+
const helper = new SharePointExcelHelper();
|
|
216
|
+
for (let i = 0; i < items.length; i++) {
|
|
217
|
+
try {
|
|
218
|
+
const siteUrl = this.getNodeParameter("siteUrl", i);
|
|
219
|
+
const filePath = this.getNodeParameter("filePath", i);
|
|
220
|
+
// Get the Excel file from SharePoint
|
|
221
|
+
const fileContent = await helper.downloadExcelFile(client, siteUrl, filePath);
|
|
222
|
+
// Load the workbook
|
|
223
|
+
const workbook = new ExcelJS.Workbook();
|
|
224
|
+
// ExcelJS.load accepts Buffer, ArrayBuffer, or Uint8Array
|
|
225
|
+
// TypeScript 5.9 has strict Buffer types - ExcelJS accepts Buffer at runtime
|
|
226
|
+
// @ts-expect-error - TypeScript 5.9 strict Buffer type checking, but ExcelJS accepts Buffer
|
|
227
|
+
await workbook.xlsx.load(fileContent);
|
|
228
|
+
if (operation === "getTableNames") {
|
|
229
|
+
// Get all table names from all worksheets
|
|
230
|
+
const tableNames = [];
|
|
231
|
+
workbook.eachSheet((worksheet) => {
|
|
232
|
+
const tables = worksheet.tables || [];
|
|
233
|
+
tables.forEach((table) => {
|
|
234
|
+
tableNames.push(table.name);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
returnData.push({
|
|
238
|
+
json: {
|
|
239
|
+
tableNames,
|
|
240
|
+
},
|
|
241
|
+
pairedItem: { item: i },
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
const tableName = this.getNodeParameter("tableName", i);
|
|
246
|
+
// Find the table in the workbook
|
|
247
|
+
let targetTable = null;
|
|
248
|
+
let targetWorksheet = null;
|
|
249
|
+
workbook.eachSheet((worksheet) => {
|
|
250
|
+
const tables = worksheet.tables || [];
|
|
251
|
+
tables.forEach((table) => {
|
|
252
|
+
if (table.name === tableName) {
|
|
253
|
+
targetTable = table;
|
|
254
|
+
targetWorksheet = worksheet;
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
if (!targetTable || !targetWorksheet) {
|
|
259
|
+
throw new Error(`Table "${tableName}" not found in the Excel file`);
|
|
260
|
+
}
|
|
261
|
+
if (operation === "getTable") {
|
|
262
|
+
// Get all data from the table
|
|
263
|
+
const tableData = helper.getTableData(targetWorksheet, targetTable);
|
|
264
|
+
returnData.push({
|
|
265
|
+
json: {
|
|
266
|
+
tableName,
|
|
267
|
+
data: tableData,
|
|
268
|
+
},
|
|
269
|
+
pairedItem: { item: i },
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
else if (operation === "lookup") {
|
|
273
|
+
// Perform lookup
|
|
274
|
+
const lookupColumn = this.getNodeParameter("lookupColumn", i);
|
|
275
|
+
const lookupValue = this.getNodeParameter("lookupValue", i);
|
|
276
|
+
const matchType = this.getNodeParameter("matchType", i);
|
|
277
|
+
const returnAll = this.getNodeParameter("returnAll", i);
|
|
278
|
+
const tableData = helper.getTableData(targetWorksheet, targetTable);
|
|
279
|
+
const matches = helper.performLookup(tableData, lookupColumn, lookupValue, matchType, returnAll);
|
|
280
|
+
if (returnAll) {
|
|
281
|
+
returnData.push({
|
|
282
|
+
json: {
|
|
283
|
+
tableName,
|
|
284
|
+
lookupColumn,
|
|
285
|
+
lookupValue,
|
|
286
|
+
matches,
|
|
287
|
+
matchCount: matches.length,
|
|
288
|
+
},
|
|
289
|
+
pairedItem: { item: i },
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
// Return each match as a separate item
|
|
294
|
+
matches.forEach((match) => {
|
|
295
|
+
returnData.push({
|
|
296
|
+
json: {
|
|
297
|
+
tableName,
|
|
298
|
+
lookupColumn,
|
|
299
|
+
lookupValue,
|
|
300
|
+
...match,
|
|
301
|
+
},
|
|
302
|
+
pairedItem: { item: i },
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch (error) {
|
|
310
|
+
if (this.continueOnFail()) {
|
|
311
|
+
returnData.push({
|
|
312
|
+
json: {
|
|
313
|
+
error: error instanceof Error ? error.message : String(error),
|
|
314
|
+
},
|
|
315
|
+
pairedItem: { item: i },
|
|
316
|
+
});
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return [returnData];
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
exports.SharePointExcel = SharePointExcel;
|
|
326
|
+
// Helper class to access private methods
|
|
327
|
+
class SharePointExcelHelper {
|
|
328
|
+
async downloadExcelFile(client, siteUrl, filePath) {
|
|
329
|
+
// Extract site ID from URL
|
|
330
|
+
const siteId = await this.getSiteId(client, siteUrl);
|
|
331
|
+
// Get drive ID
|
|
332
|
+
const drive = await client.api(`/sites/${siteId}/drive`).get();
|
|
333
|
+
const driveId = drive.id;
|
|
334
|
+
// Download the file
|
|
335
|
+
const response = await client
|
|
336
|
+
.api(`/sites/${siteId}/drives/${driveId}/root:${filePath}:/content`)
|
|
337
|
+
.getStream();
|
|
338
|
+
// Convert stream to buffer (Node.js environment)
|
|
339
|
+
const chunks = [];
|
|
340
|
+
for await (const chunk of response) {
|
|
341
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
342
|
+
}
|
|
343
|
+
return Buffer.concat(chunks);
|
|
344
|
+
}
|
|
345
|
+
async getSiteId(client, siteUrl) {
|
|
346
|
+
// Extract hostname and path from URL
|
|
347
|
+
const url = new URL(siteUrl);
|
|
348
|
+
const hostname = url.hostname;
|
|
349
|
+
const path = url.pathname;
|
|
350
|
+
// Get site by hostname and path
|
|
351
|
+
const site = await client.api(`/sites/${hostname}:${path}`).get();
|
|
352
|
+
return site.id;
|
|
353
|
+
}
|
|
354
|
+
getTableData(worksheet, table) {
|
|
355
|
+
const data = [];
|
|
356
|
+
// ExcelJS Table range structure
|
|
357
|
+
const range = table.range || {};
|
|
358
|
+
const headerRow = range.top || 1;
|
|
359
|
+
const startRow = (range.top || 1) + 1; // Skip header
|
|
360
|
+
const endRow = range.bottom || worksheet.rowCount;
|
|
361
|
+
const startCol = range.left || 1;
|
|
362
|
+
const endCol = range.right || worksheet.columnCount;
|
|
363
|
+
// Get column names from header row
|
|
364
|
+
const columnNames = [];
|
|
365
|
+
for (let col = startCol; col <= endCol; col++) {
|
|
366
|
+
const cell = worksheet.getCell(headerRow, col);
|
|
367
|
+
columnNames.push(cell.value?.toString() || `Column${col}`);
|
|
368
|
+
}
|
|
369
|
+
// Get data rows
|
|
370
|
+
for (let row = startRow; row <= endRow; row++) {
|
|
371
|
+
const rowData = {};
|
|
372
|
+
for (let col = startCol; col <= endCol; col++) {
|
|
373
|
+
const cell = worksheet.getCell(row, col);
|
|
374
|
+
const columnName = columnNames[col - startCol];
|
|
375
|
+
rowData[columnName] = this.getCellValue(cell);
|
|
376
|
+
}
|
|
377
|
+
data.push(rowData);
|
|
378
|
+
}
|
|
379
|
+
return data;
|
|
380
|
+
}
|
|
381
|
+
getCellValue(cell) {
|
|
382
|
+
if (cell.value === null || cell.value === undefined) {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
if (typeof cell.value === "object" && "text" in cell.value) {
|
|
386
|
+
return cell.value.text;
|
|
387
|
+
}
|
|
388
|
+
if (cell.value instanceof Date) {
|
|
389
|
+
return cell.value.toISOString();
|
|
390
|
+
}
|
|
391
|
+
return cell.value;
|
|
392
|
+
}
|
|
393
|
+
performLookup(tableData, lookupColumn, lookupValue, matchType, returnAll) {
|
|
394
|
+
const matches = [];
|
|
395
|
+
for (const row of tableData) {
|
|
396
|
+
const cellValue = row[lookupColumn];
|
|
397
|
+
if (cellValue === null || cellValue === undefined) {
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
const cellValueStr = String(cellValue).toLowerCase();
|
|
401
|
+
const lookupValueStr = String(lookupValue).toLowerCase();
|
|
402
|
+
let isMatch = false;
|
|
403
|
+
switch (matchType) {
|
|
404
|
+
case "exact":
|
|
405
|
+
isMatch = cellValueStr === lookupValueStr;
|
|
406
|
+
break;
|
|
407
|
+
case "contains":
|
|
408
|
+
isMatch = cellValueStr.includes(lookupValueStr);
|
|
409
|
+
break;
|
|
410
|
+
case "startsWith":
|
|
411
|
+
isMatch = cellValueStr.startsWith(lookupValueStr);
|
|
412
|
+
break;
|
|
413
|
+
case "endsWith":
|
|
414
|
+
isMatch = cellValueStr.endsWith(lookupValueStr);
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
if (isMatch) {
|
|
418
|
+
matches.push(row);
|
|
419
|
+
if (!returnAll && matches.length === 1) {
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
return matches;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
|
2
|
+
<!-- Excel logo inspired design in blue -->
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="blueGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
5
|
+
<stop offset="0%" style="stop-color:#2171B5;stop-opacity:1" />
|
|
6
|
+
<stop offset="100%" style="stop-color:#185A9D;stop-opacity:1" />
|
|
7
|
+
</linearGradient>
|
|
8
|
+
</defs>
|
|
9
|
+
|
|
10
|
+
<!-- Main rectangle with folded corner -->
|
|
11
|
+
<path d="M 10 10 L 90 10 L 90 90 L 10 90 Z M 10 10 L 25 10 L 10 25 Z"
|
|
12
|
+
fill="url(#blueGradient)" stroke="#0F4C75" stroke-width="1"/>
|
|
13
|
+
|
|
14
|
+
<!-- Folded corner highlight -->
|
|
15
|
+
<path d="M 10 10 L 25 10 L 10 25 Z" fill="#2E86AB" opacity="0.6"/>
|
|
16
|
+
|
|
17
|
+
<!-- White X on the left side -->
|
|
18
|
+
<text x="20" y="60" font-family="Arial, sans-serif" font-size="45" font-weight="bold" fill="white">X</text>
|
|
19
|
+
|
|
20
|
+
<!-- Grid pattern on the right side -->
|
|
21
|
+
<g fill="none" stroke="white" stroke-width="2.5" stroke-linecap="square">
|
|
22
|
+
<!-- Vertical lines -->
|
|
23
|
+
<line x1="50" y1="20" x2="50" y2="80"/>
|
|
24
|
+
<line x1="65" y1="20" x2="65" y2="80"/>
|
|
25
|
+
<line x1="80" y1="20" x2="80" y2="80"/>
|
|
26
|
+
|
|
27
|
+
<!-- Horizontal lines -->
|
|
28
|
+
<line x1="50" y1="20" x2="80" y2="20"/>
|
|
29
|
+
<line x1="50" y1="35" x2="80" y2="35"/>
|
|
30
|
+
<line x1="50" y1="50" x2="80" y2="50"/>
|
|
31
|
+
<line x1="50" y1="65" x2="80" y2="65"/>
|
|
32
|
+
<line x1="50" y1="80" x2="80" y2="80"/>
|
|
33
|
+
</g>
|
|
34
|
+
</svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-sharepointexcel",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "N8N node for reading Excel tables from SharePoint and performing table lookups",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"n8n-nodes",
|
|
9
|
+
"sharepoint",
|
|
10
|
+
"excel",
|
|
11
|
+
"table",
|
|
12
|
+
"lookup",
|
|
13
|
+
"microsoft-graph"
|
|
14
|
+
],
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=18.0.0"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"postinstall": "chmod +x node_modules/.bin/* 2>/dev/null || true",
|
|
21
|
+
"build": "node build.js",
|
|
22
|
+
"build:ts": "node -r typescript/bin/tsc",
|
|
23
|
+
"build:icons": "node -r gulp/bin/gulp.js build:icons",
|
|
24
|
+
"dev": "node -r typescript/bin/tsc --watch",
|
|
25
|
+
"format": "prettier nodes credentials --write",
|
|
26
|
+
"lint": "eslint nodes credentials package.json",
|
|
27
|
+
"lintfix": "eslint nodes credentials package.json --fix",
|
|
28
|
+
"prepublishOnly": "npm run build"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"n8n": {
|
|
35
|
+
"n8nNodesApiVersion": 1,
|
|
36
|
+
"nodes": [
|
|
37
|
+
"dist/nodes/SharePointExcel/SharePointExcel.node.js"
|
|
38
|
+
],
|
|
39
|
+
"credentials": [
|
|
40
|
+
"dist/credentials/SharePointOAuth2Api.credentials.js"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^18.15.0",
|
|
45
|
+
"@typescript-eslint/eslint-plugin": "^8.56.0",
|
|
46
|
+
"@typescript-eslint/parser": "^8.56.0",
|
|
47
|
+
"eslint": "^10.0.1",
|
|
48
|
+
"eslint-plugin-n8n-nodes-base": "~1.16.6",
|
|
49
|
+
"gulp": "^5.0.1",
|
|
50
|
+
"n8n-workflow": "*",
|
|
51
|
+
"prettier": "^3.8.1",
|
|
52
|
+
"typescript": "~5.9.3"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@microsoft/microsoft-graph-client": "^3.0.7",
|
|
56
|
+
"exceljs": "^4.4.0",
|
|
57
|
+
"n8n-workflow": "*"
|
|
58
|
+
}
|
|
59
|
+
}
|