@bonsae/node-red-salesforce 0.4.1 → 0.6.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 CHANGED
@@ -18,7 +18,8 @@ Salesforce nodes for [Node-RED](https://nodered.org) built with [@bonsae/nrg](ht
18
18
  | **Bulk** | Bulk API 2.0 operations for large data volumes |
19
19
  | **Describe** | Retrieve SObject metadata (fields, relationships, record types) |
20
20
  | **Streaming** | Subscribe to Platform Events and Change Data Capture via Pub/Sub API (gRPC) |
21
- | **Apex** | Invoke Apex REST endpoints |
21
+ | **Apex Invocation** | Call existing Apex REST endpoints or Invocable Actions |
22
+ | **Apex Code** | Deploy and execute custom inline Apex code (Invocable or REST) |
22
23
 
23
24
  ## Prerequisites
24
25
 
@@ -66,7 +67,16 @@ Set the operation (create, read, update, delete, upsert) and SObject type. Pass
66
67
 
67
68
  ### Bulk API 2.0
68
69
 
69
- For large data volumes. Pass an array of records in `msg.payload` for ingest operations, or a SOQL string for bulk queries.
70
+ For large data volumes. Supports three input types:
71
+
72
+ - **Record array**: `msg.payload` as `Record[]` for ingest operations
73
+ - **CSV string**: `msg.payload` as a CSV string
74
+ - **Readable stream**: `msg.payload` as a Node.js `Readable` stream (for streaming from files)
75
+ - **SOQL string**: `msg.payload` as a SOQL query string when operation is "query"
76
+
77
+ Operations: insert, update, upsert, delete, hardDelete, query.
78
+
79
+ For ingest operations, the output `msg.payload` contains `{ successfulResults, failedResults, unprocessedRecords }`. For queries, each record is streamed as a separate message. Job lifecycle progress is reported via the status port.
70
80
 
71
81
  ### Describe
72
82
 
@@ -78,9 +88,77 @@ Subscribes to Salesforce Platform Events and Change Data Capture events using th
78
88
 
79
89
  Configure the channel (e.g., `/event/MyEvent__e`), subscribe type (LATEST, EARLIEST, or CUSTOM with a replay ID), and batch size.
80
90
 
81
- ### Apex REST
91
+ ### Apex Invocation
92
+
93
+ Call existing Apex endpoints deployed in your Salesforce org. Select the type (REST or Invocable), then pick the endpoint or action from a dropdown that queries your org. For REST, choose the HTTP method and endpoint path. For Invocable, select the action by name. The payload is passed in `msg.payload`.
94
+
95
+ ### Apex Code
96
+
97
+ Deploy and execute custom Apex code directly from Node-RED. Select the type (Invocable Method or REST Resource), enter a class name, and write your Apex code in the built-in editor. The node automatically deploys the class to Salesforce on Node-RED deploy and removes it when the node is deleted.
98
+
99
+ You write the `run()` method and any helper methods or inner classes. The node wraps your code in the required Salesforce boilerplate (annotations, Input/Output classes, etc.):
100
+
101
+ ```apex
102
+ private static Object run(String payload) {
103
+ Map<String, Object> data = (Map<String, Object>) JSON.deserializeUntyped(payload);
104
+ String name = (String) data.get('name');
105
+ return 'Hello, ' + name + '!';
106
+ }
107
+ ```
108
+
109
+ You can add helper methods and inner classes alongside `run()`:
110
+
111
+ ```apex
112
+ private static Object run(String payload) {
113
+ Processor p = new Processor();
114
+ return p.process(payload);
115
+ }
116
+
117
+ public class Processor {
118
+ public String process(String input) {
119
+ return input.reverse();
120
+ }
121
+ }
122
+ ```
123
+
124
+ The class name prefix defaults to `NRG_` and can be overridden in Node-RED `settings.js`:
125
+
126
+ ```javascript
127
+ salesforceApexCodeClassPrefix: "MY_"
128
+ ```
129
+
130
+ For REST Resource mode, you also configure the URL mapping (e.g., `/my/endpoint/*`) and HTTP method.
131
+
132
+ #### Apex Language Server (optional)
133
+
134
+ The Apex Code editor supports an optional Language Server that provides autocompletions, hover documentation, signature help, diagnostics, and SObject field completions from your connected org.
135
+
136
+ **Prerequisites**: Java 11+
137
+
138
+ **Setup**:
139
+
140
+ ```bash
141
+ # 1. Download the Apex Language Server JAR (one-time)
142
+ chmod +x scripts/setup-apex-lsp.sh
143
+ ./scripts/setup-apex-lsp.sh
144
+
145
+ # 2. Start the WebSocket bridge
146
+ node scripts/apex-lsp-server.mjs
147
+ ```
148
+
149
+ **Configure** in Node-RED `settings.js`:
150
+
151
+ ```javascript
152
+ salesforceApexCodeLanguageServerUrl: "ws://localhost:3001"
153
+ ```
82
154
 
83
- Invoke custom Apex REST endpoints. Set the HTTP method and path (e.g., `/MyEndpoint`). For POST/PUT/PATCH, the request body is taken from `msg.payload`.
155
+ The bridge spawns a Java LSP process per editor session and creates a temporary SFDX workspace for type resolution. SObject definitions are fetched from the connected org and cached for 30 minutes.
156
+
157
+ The bridge accepts `--port` and `--jar` flags:
158
+
159
+ ```bash
160
+ node scripts/apex-lsp-server.mjs --port 3002 --jar /path/to/apex-jorje-lsp.jar
161
+ ```
84
162
 
85
163
  ## Development
86
164
 
Binary file
package/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import { Connection } from 'jsforce';
3
3
  import { Infer } from '@bonsae/nrg/server';
4
4
  import { IONode } from '@bonsae/nrg/server';
5
5
  import { ModuleDefinition } from '@bonsae/nrg/server';
6
+ import { Readable } from 'stream';
6
7
  import { RED } from '@bonsae/nrg/server';
7
8
  import { Schema } from '@bonsae/nrg/server';
8
9
  import { TAny } from '@sinclair/typebox';
@@ -17,38 +18,44 @@ import { TString } from '@sinclair/typebox';
17
18
  import { TTypedInput } from '@bonsae/nrg/server';
18
19
  import { TUnion } from '@sinclair/typebox';
19
20
 
20
- declare type Config = Infer<typeof SalesforceApexConfigSchema>;
21
+ declare type Config = Infer<typeof SalesforceApexCodeConfigSchema>;
21
22
 
22
23
  declare type Config_2 = Infer<typeof SalesforceConnectionConfigSchema>;
23
24
 
24
- declare type Config_3 = Infer<typeof SalesforceBulkConfigSchema>;
25
+ declare type Config_3 = Infer<typeof SalesforceApexInvocationConfigSchema>;
25
26
 
26
- declare type Config_4 = Infer<typeof SalesforceDescribeConfigSchema>;
27
+ declare type Config_4 = Infer<typeof SalesforceBulkConfigSchema>;
27
28
 
28
- declare type Config_5 = Infer<typeof SalesforceDmlConfigSchema>;
29
+ declare type Config_5 = Infer<typeof SalesforceDescribeConfigSchema>;
29
30
 
30
- declare type Config_6 = Infer<typeof SalesforceSoqlConfigSchema>;
31
+ declare type Config_6 = Infer<typeof SalesforceDmlConfigSchema>;
31
32
 
32
- declare type Config_7 = Infer<typeof SalesforceStreamingConfigSchema>;
33
+ declare type Config_7 = Infer<typeof SalesforceSoqlConfigSchema>;
34
+
35
+ declare type Config_8 = Infer<typeof SalesforceStreamingConfigSchema>;
33
36
 
34
37
  declare type Credentials = Infer<typeof SalesforceConnectionCredentialsSchema>;
35
38
 
36
39
  declare const _default: ModuleDefinition;
37
40
  export default _default;
38
41
 
39
- declare type Input = Infer<typeof SalesforceApexInputSchema>;
42
+ declare type Input = Infer<typeof SalesforceApexCodeInputSchema>;
43
+
44
+ declare type Input_2 = Infer<typeof SalesforceApexInvocationInputSchema>;
40
45
 
41
- declare type Input_2 = Infer<typeof SalesforceBulkInputSchema>;
46
+ declare type Input_3 = {
47
+ payload: string | Record<string, unknown>[] | Readable;
48
+ };
42
49
 
43
- declare type Input_3 = Infer<typeof SalesforceDescribeInputSchema>;
50
+ declare type Input_4 = Infer<typeof SalesforceDescribeInputSchema>;
44
51
 
45
- declare type Input_4 = Infer<typeof SalesforceDmlInputSchema>;
52
+ declare type Input_5 = Infer<typeof SalesforceDmlInputSchema>;
46
53
 
47
- declare type Input_5 = Infer<typeof SalesforceSoqlInputSchema>;
54
+ declare type Input_6 = Infer<typeof SalesforceSoqlInputSchema>;
48
55
 
49
- declare type Output = Infer<typeof SalesforceApexOutputsSchema>;
56
+ declare type Output = Infer<typeof SalesforceApexCodeOutputsSchema>;
50
57
 
51
- declare type Output_2 = Infer<typeof outputsSchema>;
58
+ declare type Output_2 = Infer<typeof SalesforceApexInvocationOutputsSchema>;
52
59
 
53
60
  declare type Output_3 = Infer<typeof SalesforceDescribeOutputsSchema>;
54
61
 
@@ -58,59 +65,96 @@ declare type Output_5 = Infer<typeof SalesforceSoqlOutputsSchema>;
58
65
 
59
66
  declare type Output_6 = Infer<typeof SalesforceStreamingOutputsSchema>;
60
67
 
61
- declare const outputsSchema: {
62
- record: Schema<{}>;
63
- jobCreated: Schema<{}>;
64
- };
65
-
66
- export declare class SalesforceApex extends IONode<Config, any, Input, Output> {
67
- static readonly type = "salesforce-apex";
68
+ export declare class SalesforceApexCode extends IONode<Config, any, Input, Output, Settings> {
69
+ static readonly type = "salesforce-apex-code";
68
70
  static readonly category = "salesforce";
69
71
  static readonly color: `#${string}`;
70
72
  static readonly configSchema: Schema;
71
73
  static readonly inputSchema: Schema;
72
74
  static readonly outputsSchema: Schema;
75
+ static readonly settingsSchema: Schema;
76
+ private deployed;
77
+ private getFullClassName;
78
+ private buildApexBody;
79
+ created(): Promise<void>;
73
80
  input(msg: Input): Promise<void>;
81
+ closed(removed?: boolean): Promise<void>;
82
+ }
83
+
84
+ export declare const SalesforceApexCodeConfigSchema: Schema< {
85
+ name: TString;
86
+ connection: TNodeRef<SalesforceConnection>;
87
+ apexType: TUnion<[TLiteral<"invocable">, TLiteral<"rest">]>;
88
+ className: TString;
89
+ urlMapping: TString;
90
+ httpMethod: TUnion<[TLiteral<"HttpGet">, TLiteral<"HttpPost">, TLiteral<"HttpPut">, TLiteral<"HttpPatch">, TLiteral<"HttpDelete">]>;
91
+ code: TString;
92
+ errorPort: TBoolean;
93
+ completePort: TBoolean;
94
+ statusPort: TBoolean;
95
+ }>;
96
+
97
+ export declare const SalesforceApexCodeInputSchema: Schema< {
98
+ payload: TAny;
99
+ }>;
100
+
101
+ export declare const SalesforceApexCodeOutputsSchema: Schema< {
102
+ payload: TAny;
103
+ }>;
104
+
105
+ export declare const SalesforceApexCodeSettingsSchema: Schema< {
106
+ classPrefix: TString;
107
+ languageServerUrl: TString;
108
+ }>;
109
+
110
+ export declare class SalesforceApexInvocation extends IONode<Config_3, any, Input_2, Output_2> {
111
+ static readonly type = "salesforce-apex-invocation";
112
+ static readonly category = "salesforce";
113
+ static readonly color: `#${string}`;
114
+ static readonly configSchema: Schema;
115
+ static readonly inputSchema: Schema;
116
+ static readonly outputsSchema: Schema;
117
+ input(msg: Input_2): Promise<void>;
74
118
  }
75
119
 
76
- export declare const SalesforceApexConfigSchema: Schema< {
120
+ export declare const SalesforceApexInvocationConfigSchema: Schema< {
77
121
  name: TString;
78
122
  connection: TNodeRef<SalesforceConnection>;
123
+ apexType: TUnion<[TLiteral<"rest">, TLiteral<"invocable">]>;
79
124
  method: TUnion<[TLiteral<"GET">, TLiteral<"POST">, TLiteral<"PUT">, TLiteral<"PATCH">, TLiteral<"DELETE">]>;
80
125
  path: TTypedInput<string>;
126
+ actionName: TString;
81
127
  errorPort: TBoolean;
82
128
  completePort: TBoolean;
83
129
  statusPort: TBoolean;
84
130
  }>;
85
131
 
86
- export declare const SalesforceApexInputSchema: Schema< {
132
+ export declare const SalesforceApexInvocationInputSchema: Schema< {
87
133
  payload: TAny;
88
134
  }>;
89
135
 
90
- export declare const SalesforceApexOutputsSchema: Schema< {
136
+ export declare const SalesforceApexInvocationOutputsSchema: Schema< {
91
137
  payload: TAny;
92
138
  }>;
93
139
 
94
- export declare class SalesforceBulk extends IONode<Config_3, any, Input_2, Output_2> {
140
+ export declare class SalesforceBulk extends IONode<Config_4, any, Input_3> {
95
141
  static readonly type = "salesforce-bulk";
96
142
  static readonly category = "salesforce";
97
143
  static readonly color: `#${string}`;
98
144
  static readonly configSchema: Schema;
99
145
  static readonly inputSchema: Schema;
100
- static readonly outputsSchema: {
101
- record: Schema<{}>;
102
- jobCreated: Schema<{}>;
103
- };
104
- private sendRecord;
105
- private sendJobCreated;
106
- input(msg: Input_2): Promise<void>;
146
+ static readonly outputsSchema: Schema;
147
+ input(msg: Input_3): Promise<void>;
148
+ private executeQuery;
149
+ private executeIngest;
107
150
  }
108
151
 
109
152
  export declare const SalesforceBulkConfigSchema: Schema< {
110
153
  name: TString;
111
154
  connection: TNodeRef<SalesforceConnection>;
112
- operation: TUnion<[TLiteral<"insert">, TLiteral<"update">, TLiteral<"upsert">, TLiteral<"delete">, TLiteral<"query">]>;
155
+ operation: TUnion<[TLiteral<"insert">, TLiteral<"update">, TLiteral<"upsert">, TLiteral<"delete">, TLiteral<"hardDelete">, TLiteral<"query">]>;
113
156
  sObjectType: TTypedInput<string>;
157
+ query: TTypedInput<string>;
114
158
  externalIdField: TOptional<TString>;
115
159
  assignmentRuleId: TOptional<TString>;
116
160
  columnDelimiter: TUnion<[TLiteral<"COMMA">, TLiteral<"TAB">, TLiteral<"PIPE">, TLiteral<"SEMICOLON">, TLiteral<"CARET">, TLiteral<"BACKQUOTE">]>;
@@ -126,6 +170,10 @@ export declare const SalesforceBulkInputSchema: Schema< {
126
170
  payload: TAny;
127
171
  }>;
128
172
 
173
+ export declare const SalesforceBulkOutputsSchema: Schema< {
174
+ payload: TAny;
175
+ }>;
176
+
129
177
  export declare class SalesforceConnection extends ConfigNode<Config_2, Credentials> {
130
178
  static readonly type = "salesforce-connection";
131
179
  static readonly configSchema: Schema;
@@ -152,14 +200,14 @@ refreshToken: TString;
152
200
  instanceUrl: TString;
153
201
  }>;
154
202
 
155
- export declare class SalesforceDescribe extends IONode<Config_4, any, Input_3, Output_3> {
203
+ export declare class SalesforceDescribe extends IONode<Config_5, any, Input_4, Output_3> {
156
204
  static readonly type = "salesforce-describe";
157
205
  static readonly category = "salesforce";
158
206
  static readonly color: `#${string}`;
159
207
  static readonly configSchema: Schema;
160
208
  static readonly inputSchema: Schema;
161
209
  static readonly outputsSchema: Schema;
162
- input(msg: Input_3): Promise<void>;
210
+ input(msg: Input_4): Promise<void>;
163
211
  }
164
212
 
165
213
  export declare const SalesforceDescribeConfigSchema: Schema< {
@@ -184,14 +232,14 @@ recordTypeInfos: TArray<TAny>;
184
232
  }>;
185
233
  }>;
186
234
 
187
- export declare class SalesforceDml extends IONode<Config_5, any, Input_4, Output_4> {
235
+ export declare class SalesforceDml extends IONode<Config_6, any, Input_5, Output_4> {
188
236
  static readonly type = "salesforce-dml";
189
237
  static readonly category = "salesforce";
190
238
  static readonly color: `#${string}`;
191
239
  static readonly configSchema: Schema;
192
240
  static readonly inputSchema: Schema;
193
241
  static readonly outputsSchema: Schema;
194
- input(msg: Input_4): Promise<void>;
242
+ input(msg: Input_5): Promise<void>;
195
243
  }
196
244
 
197
245
  export declare const SalesforceDmlConfigSchema: Schema< {
@@ -214,14 +262,14 @@ export declare const SalesforceDmlOutputsSchema: Schema< {
214
262
  payload: TAny;
215
263
  }>;
216
264
 
217
- export declare class SalesforceSoql extends IONode<Config_6, any, Input_5, Output_5> {
265
+ export declare class SalesforceSoql extends IONode<Config_7, any, Input_6, Output_5> {
218
266
  static readonly type = "salesforce-soql";
219
267
  static readonly category = "salesforce";
220
268
  static readonly color: `#${string}`;
221
269
  static readonly configSchema: Schema;
222
270
  static readonly inputSchema: Schema;
223
271
  static readonly outputsSchema: Schema;
224
- input(msg: Input_5): Promise<void>;
272
+ input(msg: Input_6): Promise<void>;
225
273
  }
226
274
 
227
275
  export declare const SalesforceSoqlConfigSchema: Schema< {
@@ -243,7 +291,7 @@ totalSize: TNumber;
243
291
  done: TBoolean;
244
292
  }>;
245
293
 
246
- export declare class SalesforceStreaming extends IONode<Config_7, any, any, Output_6> {
294
+ export declare class SalesforceStreaming extends IONode<Config_8, any, any, Output_6> {
247
295
  static readonly type = "salesforce-streaming";
248
296
  static readonly category = "salesforce";
249
297
  static readonly color: `#${string}`;
@@ -281,4 +329,6 @@ channel: TString;
281
329
  topic: TString;
282
330
  }>;
283
331
 
332
+ declare type Settings = Infer<typeof SalesforceApexCodeSettingsSchema>;
333
+
284
334
  export { }
package/index.html CHANGED
@@ -63,4 +63,5 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
63
63
  LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK.
64
64
 
65
65
  -->
66
- <script type="module" src="resources/@bonsae/node-red-salesforce/index.FDBQSjT7.js" defer></script>
66
+ <script type="module" src="resources/@bonsae/node-red-salesforce/index.C1dzx4s2.js" defer></script>
67
+ <link rel="stylesheet" href="resources/@bonsae/node-red-salesforce/index.zXteTlSH.css">