@jsforce/jsforce-node 0.0.1 → 3.0.0-next.1

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.
Files changed (127) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +54 -0
  3. package/index.d.ts +4 -0
  4. package/index.js +1 -0
  5. package/lib/VERSION.d.ts +2 -0
  6. package/lib/VERSION.js +3 -0
  7. package/lib/api/analytics/types.d.ts +509 -0
  8. package/lib/api/analytics/types.js +2 -0
  9. package/lib/api/analytics.d.ts +163 -0
  10. package/lib/api/analytics.js +342 -0
  11. package/lib/api/apex.d.ts +44 -0
  12. package/lib/api/apex.js +86 -0
  13. package/lib/api/bulk.d.ts +444 -0
  14. package/lib/api/bulk.js +1372 -0
  15. package/lib/api/chatter.d.ts +133 -0
  16. package/lib/api/chatter.js +248 -0
  17. package/lib/api/metadata/schema.d.ts +16117 -0
  18. package/lib/api/metadata/schema.js +9094 -0
  19. package/lib/api/metadata.d.ts +189 -0
  20. package/lib/api/metadata.js +406 -0
  21. package/lib/api/soap/schema.d.ts +3167 -0
  22. package/lib/api/soap/schema.js +1787 -0
  23. package/lib/api/soap.d.ts +76 -0
  24. package/lib/api/soap.js +155 -0
  25. package/lib/api/streaming/extension.d.ts +94 -0
  26. package/lib/api/streaming/extension.js +151 -0
  27. package/lib/api/streaming.d.ts +160 -0
  28. package/lib/api/streaming.js +252 -0
  29. package/lib/api/tooling.d.ts +284 -0
  30. package/lib/api/tooling.js +202 -0
  31. package/lib/api/wsdl/wsdl2schema.d.ts +1 -0
  32. package/lib/api/wsdl/wsdl2schema.js +354 -0
  33. package/lib/browser/canvas.d.ts +12 -0
  34. package/lib/browser/canvas.js +77 -0
  35. package/lib/browser/client.d.ts +82 -0
  36. package/lib/browser/client.js +244 -0
  37. package/lib/browser/jsonp.d.ts +12 -0
  38. package/lib/browser/jsonp.js +69 -0
  39. package/lib/browser/registry.d.ts +3 -0
  40. package/lib/browser/registry.js +5 -0
  41. package/lib/browser/request.d.ts +10 -0
  42. package/lib/browser/request.js +202 -0
  43. package/lib/cache.d.ts +74 -0
  44. package/lib/cache.js +159 -0
  45. package/lib/connection.d.ts +355 -0
  46. package/lib/connection.js +1153 -0
  47. package/lib/core.d.ts +17 -0
  48. package/lib/core.js +55 -0
  49. package/lib/csv.d.ts +23 -0
  50. package/lib/csv.js +35 -0
  51. package/lib/date.d.ts +82 -0
  52. package/lib/date.js +201 -0
  53. package/lib/http-api.d.ts +75 -0
  54. package/lib/http-api.js +257 -0
  55. package/lib/index.d.ts +12 -0
  56. package/lib/index.js +31 -0
  57. package/lib/jsforce.d.ts +26 -0
  58. package/lib/jsforce.js +67 -0
  59. package/lib/jwtOAuth2.d.ts +8 -0
  60. package/lib/jwtOAuth2.js +23 -0
  61. package/lib/oauth2.d.ts +92 -0
  62. package/lib/oauth2.js +245 -0
  63. package/lib/process.d.ts +157 -0
  64. package/lib/process.js +143 -0
  65. package/lib/query.d.ts +341 -0
  66. package/lib/query.js +817 -0
  67. package/lib/quick-action.d.ts +44 -0
  68. package/lib/quick-action.js +46 -0
  69. package/lib/record-reference.d.ts +46 -0
  70. package/lib/record-reference.js +65 -0
  71. package/lib/record-stream.d.ts +83 -0
  72. package/lib/record-stream.js +233 -0
  73. package/lib/registry/base.d.ts +43 -0
  74. package/lib/registry/base.js +96 -0
  75. package/lib/registry/empty.d.ts +7 -0
  76. package/lib/registry/empty.js +13 -0
  77. package/lib/registry/file.d.ts +11 -0
  78. package/lib/registry/file.js +51 -0
  79. package/lib/registry/index.d.ts +8 -0
  80. package/lib/registry/index.js +21 -0
  81. package/lib/registry/sfdx.d.ts +56 -0
  82. package/lib/registry/sfdx.js +133 -0
  83. package/lib/registry/types.d.ts +47 -0
  84. package/lib/registry/types.js +2 -0
  85. package/lib/request-helper.d.ts +23 -0
  86. package/lib/request-helper.js +102 -0
  87. package/lib/request.d.ts +11 -0
  88. package/lib/request.js +75 -0
  89. package/lib/session-refresh-delegate.d.ts +31 -0
  90. package/lib/session-refresh-delegate.js +69 -0
  91. package/lib/soap.d.ts +60 -0
  92. package/lib/soap.js +246 -0
  93. package/lib/sobject.d.ts +258 -0
  94. package/lib/sobject.js +376 -0
  95. package/lib/soql-builder.d.ts +25 -0
  96. package/lib/soql-builder.js +226 -0
  97. package/lib/transport.d.ts +63 -0
  98. package/lib/transport.js +175 -0
  99. package/lib/types/common.d.ts +560 -0
  100. package/lib/types/common.js +2 -0
  101. package/lib/types/index.d.ts +7 -0
  102. package/lib/types/index.js +23 -0
  103. package/lib/types/projection.d.ts +26 -0
  104. package/lib/types/projection.js +2 -0
  105. package/lib/types/record.d.ts +44 -0
  106. package/lib/types/record.js +2 -0
  107. package/lib/types/schema.d.ts +50 -0
  108. package/lib/types/schema.js +2 -0
  109. package/lib/types/soap.d.ts +43 -0
  110. package/lib/types/soap.js +2 -0
  111. package/lib/types/standard-schema.d.ts +16199 -0
  112. package/lib/types/standard-schema.js +2 -0
  113. package/lib/types/util.d.ts +7 -0
  114. package/lib/types/util.js +2 -0
  115. package/lib/util/formatter.d.ts +8 -0
  116. package/lib/util/formatter.js +24 -0
  117. package/lib/util/function.d.ts +32 -0
  118. package/lib/util/function.js +52 -0
  119. package/lib/util/logger.d.ts +29 -0
  120. package/lib/util/logger.js +102 -0
  121. package/lib/util/promise.d.ts +19 -0
  122. package/lib/util/promise.js +25 -0
  123. package/lib/util/stream.d.ts +12 -0
  124. package/lib/util/stream.js +88 -0
  125. package/package.json +260 -6
  126. package/typings/faye/index.d.ts +16 -0
  127. package/typings/index.d.ts +1 -0
package/lib/query.js ADDED
@@ -0,0 +1,817 @@
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 (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.SubQuery = exports.Query = exports.ResponseTargets = void 0;
27
+ /**
28
+ * @file Manages query for records in Salesforce
29
+ * @author Shinichi Tomita <shinichi.tomita@gmail.com>
30
+ */
31
+ const events_1 = require("events");
32
+ const logger_1 = require("./util/logger");
33
+ const record_stream_1 = __importStar(require("./record-stream"));
34
+ const soql_builder_1 = require("./soql-builder");
35
+ const ResponseTargetValues = [
36
+ 'QueryResult',
37
+ 'Records',
38
+ 'SingleRecord',
39
+ 'Count',
40
+ ];
41
+ exports.ResponseTargets = ResponseTargetValues.reduce((values, target) => ({ ...values, [target]: target }), {});
42
+ /**
43
+ *
44
+ */
45
+ const DEFAULT_BULK_THRESHOLD = 200;
46
+ const DEFAULT_BULK_API_VERSION = 1;
47
+ /**
48
+ * Query
49
+ */
50
+ class Query extends events_1.EventEmitter {
51
+ static _logger = (0, logger_1.getLogger)('query');
52
+ _conn;
53
+ _logger;
54
+ _soql;
55
+ _locator;
56
+ _config = {};
57
+ _children = [];
58
+ _options;
59
+ _executed = false;
60
+ _finished = false;
61
+ _chaining = false;
62
+ _promise;
63
+ _stream;
64
+ totalSize = 0;
65
+ totalFetched = 0;
66
+ records = [];
67
+ /**
68
+ *
69
+ */
70
+ constructor(conn, config, options) {
71
+ super();
72
+ this._conn = conn;
73
+ this._logger = conn._logLevel
74
+ ? Query._logger.createInstance(conn._logLevel)
75
+ : Query._logger;
76
+ if (typeof config === 'string') {
77
+ this._soql = config;
78
+ this._logger.debug(`config is soql: ${config}`);
79
+ }
80
+ else if (typeof config.locator === 'string') {
81
+ const locator = config.locator;
82
+ this._logger.debug(`config is locator: ${locator}`);
83
+ this._locator = locator.includes('/')
84
+ ? this.urlToLocator(locator)
85
+ : locator;
86
+ }
87
+ else {
88
+ this._logger.debug(`config is QueryConfig: ${config}`);
89
+ const { fields, includes, sort, ..._config } = config;
90
+ this._config = _config;
91
+ this.select(fields);
92
+ if (includes) {
93
+ this.includeChildren(includes);
94
+ }
95
+ if (sort) {
96
+ this.sort(sort);
97
+ }
98
+ }
99
+ this._options = {
100
+ headers: {},
101
+ maxFetch: 10000,
102
+ autoFetch: false,
103
+ scanAll: false,
104
+ responseTarget: 'QueryResult',
105
+ ...(options || {}),
106
+ };
107
+ // promise instance
108
+ this._promise = new Promise((resolve, reject) => {
109
+ this.on('response', resolve);
110
+ this.on('error', reject);
111
+ });
112
+ this._stream = new record_stream_1.Serializable();
113
+ this.on('record', (record) => this._stream.push(record));
114
+ this.on('end', () => this._stream.push(null));
115
+ this.on('error', (err) => {
116
+ try {
117
+ this._stream.emit('error', err);
118
+ }
119
+ catch (e) {
120
+ // eslint-disable-line no-empty
121
+ }
122
+ });
123
+ }
124
+ /**
125
+ * Select fields to include in the returning result
126
+ */
127
+ select(fields = '*') {
128
+ if (this._soql) {
129
+ throw Error('Cannot set select fields for the query which has already built SOQL.');
130
+ }
131
+ function toFieldArray(fields) {
132
+ return typeof fields === 'string'
133
+ ? fields.split(/\s*,\s*/)
134
+ : Array.isArray(fields)
135
+ ? fields
136
+ .map(toFieldArray)
137
+ .reduce((fs, f) => [...fs, ...f], [])
138
+ : Object.entries(fields)
139
+ .map(([f, v]) => {
140
+ if (typeof v === 'number' || typeof v === 'boolean') {
141
+ return v ? [f] : [];
142
+ }
143
+ else {
144
+ return toFieldArray(v).map((p) => `${f}.${p}`);
145
+ }
146
+ })
147
+ .reduce((fs, f) => [...fs, ...f], []);
148
+ }
149
+ if (fields) {
150
+ this._config.fields = toFieldArray(fields);
151
+ }
152
+ // force convert query record type without changing instance;
153
+ return this;
154
+ }
155
+ /**
156
+ * Set query conditions to filter the result records
157
+ */
158
+ where(conditions) {
159
+ if (this._soql) {
160
+ throw Error('Cannot set where conditions for the query which has already built SOQL.');
161
+ }
162
+ this._config.conditions = conditions;
163
+ return this;
164
+ }
165
+ /**
166
+ * Limit the returning result
167
+ */
168
+ limit(limit) {
169
+ if (this._soql) {
170
+ throw Error('Cannot set limit for the query which has already built SOQL.');
171
+ }
172
+ this._config.limit = limit;
173
+ return this;
174
+ }
175
+ /**
176
+ * Skip records
177
+ */
178
+ skip(offset) {
179
+ if (this._soql) {
180
+ throw Error('Cannot set skip/offset for the query which has already built SOQL.');
181
+ }
182
+ this._config.offset = offset;
183
+ return this;
184
+ }
185
+ /**
186
+ * Synonym of Query#skip()
187
+ */
188
+ offset = this.skip;
189
+ sort(sort, dir) {
190
+ if (this._soql) {
191
+ throw Error('Cannot set sort for the query which has already built SOQL.');
192
+ }
193
+ if (typeof sort === 'string' && typeof dir !== 'undefined') {
194
+ this._config.sort = [[sort, dir]];
195
+ }
196
+ else {
197
+ this._config.sort = sort;
198
+ }
199
+ return this;
200
+ }
201
+ /**
202
+ * Synonym of Query#sort()
203
+ */
204
+ orderby = this.sort;
205
+ include(childRelName, conditions, fields, options = {}) {
206
+ if (this._soql) {
207
+ throw Error('Cannot include child relationship into the query which has already built SOQL.');
208
+ }
209
+ const childConfig = {
210
+ fields: fields === null ? undefined : fields,
211
+ table: childRelName,
212
+ conditions: conditions === null ? undefined : conditions,
213
+ limit: options.limit,
214
+ offset: options.offset,
215
+ sort: options.sort,
216
+ };
217
+ // eslint-disable-next-line no-use-before-define
218
+ const childQuery = new SubQuery(this._conn, childRelName, childConfig, this);
219
+ this._children.push(childQuery);
220
+ return childQuery;
221
+ }
222
+ /**
223
+ * Include child relationship queries, but not moving down to the children context
224
+ */
225
+ includeChildren(includes) {
226
+ if (this._soql) {
227
+ throw Error('Cannot include child relationship into the query which has already built SOQL.');
228
+ }
229
+ for (const crname of Object.keys(includes)) {
230
+ const { conditions, fields, ...options } = includes[crname];
231
+ this.include(crname, conditions, fields, options);
232
+ }
233
+ return this;
234
+ }
235
+ /**
236
+ * Setting maxFetch query option
237
+ */
238
+ maxFetch(maxFetch) {
239
+ this._options.maxFetch = maxFetch;
240
+ return this;
241
+ }
242
+ /**
243
+ * Switching auto fetch mode
244
+ */
245
+ autoFetch(autoFetch) {
246
+ this._options.autoFetch = autoFetch;
247
+ return this;
248
+ }
249
+ /**
250
+ * Set flag to scan all records including deleted and archived.
251
+ */
252
+ scanAll(scanAll) {
253
+ this._options.scanAll = scanAll;
254
+ return this;
255
+ }
256
+ /**
257
+ *
258
+ */
259
+ setResponseTarget(responseTarget) {
260
+ if (responseTarget in exports.ResponseTargets) {
261
+ this._options.responseTarget = responseTarget;
262
+ }
263
+ // force change query response target without changing instance
264
+ return this;
265
+ }
266
+ /**
267
+ * Execute query and fetch records from server.
268
+ */
269
+ execute(options_ = {}) {
270
+ if (this._executed) {
271
+ throw new Error('re-executing already executed query');
272
+ }
273
+ if (this._finished) {
274
+ throw new Error('executing already closed query');
275
+ }
276
+ const options = {
277
+ headers: options_.headers || this._options.headers,
278
+ responseTarget: options_.responseTarget || this._options.responseTarget,
279
+ autoFetch: options_.autoFetch || this._options.autoFetch,
280
+ maxFetch: options_.maxFetch || this._options.maxFetch,
281
+ scanAll: options_.scanAll || this._options.scanAll,
282
+ };
283
+ // collect fetched records in array
284
+ // only when response target is Records and
285
+ // either callback or chaining promises are available to this query.
286
+ this.once('fetch', () => {
287
+ if (options.responseTarget === exports.ResponseTargets.Records &&
288
+ this._chaining) {
289
+ this._logger.debug('--- collecting all fetched records ---');
290
+ const records = [];
291
+ const onRecord = (record) => records.push(record);
292
+ this.on('record', onRecord);
293
+ this.once('end', () => {
294
+ this.removeListener('record', onRecord);
295
+ this.emit('response', records, this);
296
+ });
297
+ }
298
+ });
299
+ // flag to prevent re-execution
300
+ this._executed = true;
301
+ (async () => {
302
+ // start actual query
303
+ this._logger.debug('>>> Query start >>>');
304
+ try {
305
+ await this._execute(options);
306
+ this._logger.debug('*** Query finished ***');
307
+ }
308
+ catch (error) {
309
+ this._logger.debug('--- Query error ---', error);
310
+ this.emit('error', error);
311
+ }
312
+ })();
313
+ // return Query instance for chaining
314
+ return this;
315
+ }
316
+ /**
317
+ * Synonym of Query#execute()
318
+ */
319
+ exec = this.execute;
320
+ /**
321
+ * Synonym of Query#execute()
322
+ */
323
+ run = this.execute;
324
+ locatorToUrl() {
325
+ return this._locator
326
+ ? [this._conn._baseUrl(), '/query/', this._locator].join('')
327
+ : '';
328
+ }
329
+ urlToLocator(url) {
330
+ return url.split('/').pop();
331
+ }
332
+ constructResponse(rawDone, responseTarget) {
333
+ switch (responseTarget) {
334
+ case 'Count':
335
+ return this.totalSize;
336
+ case 'SingleRecord':
337
+ return this.records?.[0] ?? null;
338
+ case 'Records':
339
+ return this.records;
340
+ // QueryResult is default response target
341
+ default:
342
+ return {
343
+ ...{
344
+ records: this.records,
345
+ totalSize: this.totalSize,
346
+ done: rawDone ?? true, // when no records, done is omitted
347
+ },
348
+ ...(this._locator ? { nextRecordsUrl: this.locatorToUrl() } : {}),
349
+ };
350
+ }
351
+ }
352
+ /**
353
+ * @private
354
+ */
355
+ async _execute(options) {
356
+ const { headers, responseTarget, autoFetch, maxFetch, scanAll } = options;
357
+ this._logger.debug('execute with options', options);
358
+ let url;
359
+ if (this._locator) {
360
+ url = this.locatorToUrl();
361
+ }
362
+ else {
363
+ const soql = await this.toSOQL();
364
+ this._logger.debug(`SOQL = ${soql}`);
365
+ url = [
366
+ this._conn._baseUrl(),
367
+ '/',
368
+ scanAll ? 'queryAll' : 'query',
369
+ '?q=',
370
+ encodeURIComponent(soql),
371
+ ].join('');
372
+ }
373
+ const data = await this._conn.request({ method: 'GET', url, headers });
374
+ this.emit('fetch');
375
+ this.totalSize = data.totalSize;
376
+ this.records = this.records?.concat(maxFetch - this.records.length > data.records.length
377
+ ? data.records
378
+ : data.records.slice(0, maxFetch - this.records.length));
379
+ this._locator = data.nextRecordsUrl
380
+ ? this.urlToLocator(data.nextRecordsUrl)
381
+ : undefined;
382
+ this._finished =
383
+ this._finished ||
384
+ data.done ||
385
+ !autoFetch ||
386
+ // this is what the response looks like when there are no results
387
+ (data.records.length === 0 && data.done === undefined);
388
+ // streaming record instances
389
+ const numRecords = data.records?.length ?? 0;
390
+ let totalFetched = this.totalFetched;
391
+ for (let i = 0; i < numRecords; i++) {
392
+ if (totalFetched >= maxFetch) {
393
+ this._finished = true;
394
+ break;
395
+ }
396
+ const record = data.records[i];
397
+ this.emit('record', record, totalFetched, this);
398
+ totalFetched += 1;
399
+ }
400
+ this.totalFetched = totalFetched;
401
+ if (this._finished) {
402
+ const response = this.constructResponse(data.done, responseTarget);
403
+ // only fire response event when it should be notified per fetch
404
+ if (responseTarget !== exports.ResponseTargets.Records) {
405
+ this.emit('response', response, this);
406
+ }
407
+ this.emit('end');
408
+ return response;
409
+ }
410
+ else {
411
+ return this._execute(options);
412
+ }
413
+ }
414
+ stream(type = 'csv') {
415
+ if (!this._finished && !this._executed) {
416
+ this.execute({ autoFetch: true });
417
+ }
418
+ return type === 'record' ? this._stream : this._stream.stream(type);
419
+ }
420
+ /**
421
+ * Pipe the queried records to another stream
422
+ * This is for backward compatibility; Query is not a record stream instance anymore in 2.0.
423
+ * If you want a record stream instance, use `Query#stream('record')`.
424
+ */
425
+ pipe(stream) {
426
+ return this.stream('record').pipe(stream);
427
+ }
428
+ /**
429
+ * @protected
430
+ */
431
+ async _expandFields(sobject_) {
432
+ if (this._soql) {
433
+ throw new Error('Cannot expand fields for the query which has already built SOQL.');
434
+ }
435
+ const { fields = [], table = '' } = this._config;
436
+ const sobject = sobject_ || table;
437
+ this._logger.debug(`_expandFields: sobject = ${sobject}, fields = ${fields.join(', ')}`);
438
+ const [efields] = await Promise.all([
439
+ this._expandAsteriskFields(sobject, fields),
440
+ ...this._children.map(async (childQuery) => {
441
+ await childQuery._expandFields();
442
+ return [];
443
+ }),
444
+ ]);
445
+ this._config.fields = efields;
446
+ this._config.includes = this._children
447
+ .map((cquery) => {
448
+ const cconfig = cquery._query._config;
449
+ return [cconfig.table, cconfig];
450
+ })
451
+ .reduce((includes, [ctable, cconfig]) => ({
452
+ ...includes,
453
+ [ctable]: cconfig,
454
+ }), {});
455
+ }
456
+ /**
457
+ *
458
+ */
459
+ async _findRelationObject(relName) {
460
+ const table = this._config.table;
461
+ if (!table) {
462
+ throw new Error('No table information provided in the query');
463
+ }
464
+ this._logger.debug(`finding table for relation "${relName}" in "${table}"...`);
465
+ const sobject = await this._conn.describe$(table);
466
+ const upperRname = relName.toUpperCase();
467
+ for (const cr of sobject.childRelationships) {
468
+ if ((cr.relationshipName || '').toUpperCase() === upperRname &&
469
+ cr.childSObject) {
470
+ return cr.childSObject;
471
+ }
472
+ }
473
+ throw new Error(`No child relationship found: ${relName}`);
474
+ }
475
+ /**
476
+ *
477
+ */
478
+ async _expandAsteriskFields(sobject, fields) {
479
+ const expandedFields = await Promise.all(fields.map(async (field) => this._expandAsteriskField(sobject, field)));
480
+ return expandedFields.reduce((eflds, flds) => [...eflds, ...flds], []);
481
+ }
482
+ /**
483
+ *
484
+ */
485
+ async _expandAsteriskField(sobject, field) {
486
+ this._logger.debug(`expanding field "${field}" in "${sobject}"...`);
487
+ const fpath = field.split('.');
488
+ if (fpath[fpath.length - 1] === '*') {
489
+ const so = await this._conn.describe$(sobject);
490
+ this._logger.debug(`table ${sobject} has been described`);
491
+ if (fpath.length > 1) {
492
+ const rname = fpath.shift();
493
+ for (const f of so.fields) {
494
+ if (f.relationshipName &&
495
+ rname &&
496
+ f.relationshipName.toUpperCase() === rname.toUpperCase()) {
497
+ const rfield = f;
498
+ const referenceTo = rfield.referenceTo || [];
499
+ const rtable = referenceTo.length === 1 ? referenceTo[0] : 'Name';
500
+ const fpaths = await this._expandAsteriskField(rtable, fpath.join('.'));
501
+ return fpaths.map((fp) => `${rname}.${fp}`);
502
+ }
503
+ }
504
+ return [];
505
+ }
506
+ return so.fields.map((f) => f.name);
507
+ }
508
+ return [field];
509
+ }
510
+ /**
511
+ * Explain plan for executing query
512
+ */
513
+ async explain() {
514
+ const soql = await this.toSOQL();
515
+ this._logger.debug(`SOQL = ${soql}`);
516
+ const url = `/query/?explain=${encodeURIComponent(soql)}`;
517
+ return this._conn.request(url);
518
+ }
519
+ /**
520
+ * Return SOQL expression for the query
521
+ */
522
+ async toSOQL() {
523
+ if (this._soql) {
524
+ return this._soql;
525
+ }
526
+ await this._expandFields();
527
+ return (0, soql_builder_1.createSOQL)(this._config);
528
+ }
529
+ /**
530
+ * Promise/A+ interface
531
+ * http://promises-aplus.github.io/promises-spec/
532
+ *
533
+ * Delegate to deferred promise, return promise instance for query result
534
+ */
535
+ then(onResolve, onReject) {
536
+ this._chaining = true;
537
+ if (!this._finished && !this._executed) {
538
+ this.execute();
539
+ }
540
+ if (!this._promise) {
541
+ throw new Error('invalid state: promise is not set after query execution');
542
+ }
543
+ return this._promise.then(onResolve, onReject);
544
+ }
545
+ catch(onReject) {
546
+ return this.then(null, onReject);
547
+ }
548
+ promise() {
549
+ // TODO(cristian): verify this is correct
550
+ return Promise.resolve(this);
551
+ }
552
+ destroy(type, options) {
553
+ if (typeof type === 'object' && type !== null) {
554
+ options = type;
555
+ type = undefined;
556
+ }
557
+ options = options || {};
558
+ const type_ = type || this._config.table;
559
+ if (!type_) {
560
+ throw new Error('SOQL based query needs SObject type information to bulk delete.');
561
+ }
562
+ // Set the threshold number to pass to bulk API
563
+ const thresholdNum = options.allowBulk === false
564
+ ? -1
565
+ : typeof options.bulkThreshold === 'number'
566
+ ? options.bulkThreshold
567
+ : // determine threshold if the connection version supports SObject collection API or not
568
+ this._conn._ensureVersion(42)
569
+ ? DEFAULT_BULK_THRESHOLD
570
+ : this._conn._maxRequest / 2;
571
+ const bulkApiVersion = options.bulkApiVersion ?? DEFAULT_BULK_API_VERSION;
572
+ return new Promise((resolve, reject) => {
573
+ const createBatch = () => this._conn
574
+ .sobject(type_)
575
+ .deleteBulk()
576
+ .on('response', resolve)
577
+ .on('error', reject);
578
+ let records = [];
579
+ let batch = null;
580
+ const handleRecord = (rec) => {
581
+ if (!rec.Id) {
582
+ const err = new Error('Queried record does not include Salesforce record ID.');
583
+ this.emit('error', err);
584
+ return;
585
+ }
586
+ const record = { Id: rec.Id };
587
+ if (batch) {
588
+ batch.write(record);
589
+ }
590
+ else {
591
+ records.push(record);
592
+ if (thresholdNum >= 0 &&
593
+ records.length > thresholdNum &&
594
+ bulkApiVersion === 1) {
595
+ // Use bulk delete instead of SObject REST API
596
+ batch = createBatch();
597
+ for (const record of records) {
598
+ batch.write(record);
599
+ }
600
+ records = [];
601
+ }
602
+ }
603
+ };
604
+ const handleEnd = () => {
605
+ if (batch) {
606
+ batch.end();
607
+ }
608
+ else {
609
+ const ids = records.map((record) => record.Id);
610
+ if (records.length > thresholdNum && bulkApiVersion === 2) {
611
+ this._conn.bulk2
612
+ .loadAndWaitForResults({
613
+ object: type_,
614
+ operation: 'delete',
615
+ input: records,
616
+ })
617
+ .then((allResults) => resolve(this.mapBulkV2ResultsToSaveResults(allResults)), reject);
618
+ }
619
+ else {
620
+ this._conn
621
+ .sobject(type_)
622
+ .destroy(ids, { allowRecursive: true })
623
+ .then(resolve, reject);
624
+ }
625
+ }
626
+ };
627
+ this.stream('record')
628
+ .on('data', handleRecord)
629
+ .on('end', handleEnd)
630
+ .on('error', reject);
631
+ });
632
+ }
633
+ /**
634
+ * Synonym of Query#destroy()
635
+ */
636
+ delete = this.destroy;
637
+ /**
638
+ * Synonym of Query#destroy()
639
+ */
640
+ del = this.destroy;
641
+ update(mapping, type, options) {
642
+ if (typeof type === 'object' && type !== null) {
643
+ options = type;
644
+ type = undefined;
645
+ }
646
+ options = options || {};
647
+ const type_ = type || (this._config && this._config.table);
648
+ if (!type_) {
649
+ throw new Error('SOQL based query needs SObject type information to bulk update.');
650
+ }
651
+ const updateStream = typeof mapping === 'function'
652
+ ? record_stream_1.default.map(mapping)
653
+ : record_stream_1.default.recordMapStream(mapping);
654
+ // Set the threshold number to pass to bulk API
655
+ const thresholdNum = options.allowBulk === false
656
+ ? -1
657
+ : typeof options.bulkThreshold === 'number'
658
+ ? options.bulkThreshold
659
+ : // determine threshold if the connection version supports SObject collection API or not
660
+ this._conn._ensureVersion(42)
661
+ ? DEFAULT_BULK_THRESHOLD
662
+ : this._conn._maxRequest / 2;
663
+ const bulkApiVersion = options.bulkApiVersion ?? DEFAULT_BULK_API_VERSION;
664
+ return new Promise((resolve, reject) => {
665
+ const createBatch = () => this._conn
666
+ .sobject(type_)
667
+ .updateBulk()
668
+ .on('response', resolve)
669
+ .on('error', reject);
670
+ let records = [];
671
+ let batch = null;
672
+ const handleRecord = (record) => {
673
+ if (batch) {
674
+ batch.write(record);
675
+ }
676
+ else {
677
+ records.push(record);
678
+ }
679
+ if (thresholdNum >= 0 &&
680
+ records.length > thresholdNum &&
681
+ bulkApiVersion === 1) {
682
+ // Use bulk update instead of SObject REST API
683
+ batch = createBatch();
684
+ for (const record of records) {
685
+ batch.write(record);
686
+ }
687
+ records = [];
688
+ }
689
+ };
690
+ const handleEnd = () => {
691
+ if (batch) {
692
+ batch.end();
693
+ }
694
+ else {
695
+ if (records.length > thresholdNum && bulkApiVersion === 2) {
696
+ this._conn.bulk2
697
+ .loadAndWaitForResults({
698
+ object: type_,
699
+ operation: 'update',
700
+ input: records,
701
+ })
702
+ .then((allResults) => resolve(this.mapBulkV2ResultsToSaveResults(allResults)), reject);
703
+ }
704
+ else {
705
+ this._conn
706
+ .sobject(type_)
707
+ .update(records, { allowRecursive: true })
708
+ .then(resolve, reject);
709
+ }
710
+ }
711
+ };
712
+ this.stream('record')
713
+ .on('error', reject)
714
+ .pipe(updateStream)
715
+ .on('data', handleRecord)
716
+ .on('end', handleEnd)
717
+ .on('error', reject);
718
+ });
719
+ }
720
+ mapBulkV2ResultsToSaveResults(bulkJobAllResults) {
721
+ const successSaveResults = bulkJobAllResults.successfulResults.map((r) => {
722
+ const saveResult = {
723
+ id: r.sf__Id,
724
+ success: true,
725
+ errors: [],
726
+ };
727
+ return saveResult;
728
+ });
729
+ const failedSaveResults = bulkJobAllResults.failedResults.map((r) => {
730
+ const saveResult = {
731
+ success: false,
732
+ errors: [
733
+ {
734
+ errorCode: r.sf__Error,
735
+ message: r.sf__Error,
736
+ },
737
+ ],
738
+ };
739
+ return saveResult;
740
+ });
741
+ return [...successSaveResults, ...failedSaveResults];
742
+ }
743
+ }
744
+ exports.Query = Query;
745
+ /*--------------------------------------------*/
746
+ /**
747
+ * SubQuery object for representing child relationship query
748
+ */
749
+ class SubQuery {
750
+ _relName;
751
+ _query;
752
+ _parent;
753
+ /**
754
+ *
755
+ */
756
+ constructor(conn, relName, config, parent) {
757
+ this._relName = relName;
758
+ this._query = new Query(conn, config);
759
+ this._parent = parent;
760
+ }
761
+ /**
762
+ *
763
+ */
764
+ select(fields) {
765
+ // force convert query record type without changing instance
766
+ this._query = this._query.select(fields);
767
+ return this;
768
+ }
769
+ /**
770
+ *
771
+ */
772
+ where(conditions) {
773
+ this._query = this._query.where(conditions);
774
+ return this;
775
+ }
776
+ /**
777
+ * Limit the returning result
778
+ */
779
+ limit(limit) {
780
+ this._query = this._query.limit(limit);
781
+ return this;
782
+ }
783
+ /**
784
+ * Skip records
785
+ */
786
+ skip(offset) {
787
+ this._query = this._query.skip(offset);
788
+ return this;
789
+ }
790
+ /**
791
+ * Synonym of SubQuery#skip()
792
+ */
793
+ offset = this.skip;
794
+ sort(sort, dir) {
795
+ this._query = this._query.sort(sort, dir);
796
+ return this;
797
+ }
798
+ /**
799
+ * Synonym of SubQuery#sort()
800
+ */
801
+ orderby = this.sort;
802
+ /**
803
+ *
804
+ */
805
+ async _expandFields() {
806
+ const sobject = await this._parent._findRelationObject(this._relName);
807
+ return this._query._expandFields(sobject);
808
+ }
809
+ /**
810
+ * Back the context to parent query object
811
+ */
812
+ end() {
813
+ return this._parent;
814
+ }
815
+ }
816
+ exports.SubQuery = SubQuery;
817
+ exports.default = Query;