@heroku/applink 1.0.0-ea

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 (40) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/LICENSE +29 -0
  3. package/README.md +62 -0
  4. package/SECURITY.md +7 -0
  5. package/TERMS_OF_USE.md +24 -0
  6. package/dist/add-ons/heroku-applink.d.ts +2 -0
  7. package/dist/add-ons/heroku-applink.js +54 -0
  8. package/dist/index.d.ts +268 -0
  9. package/dist/index.js +52 -0
  10. package/dist/sdk/bulk-api.d.ts +3 -0
  11. package/dist/sdk/bulk-api.js +422 -0
  12. package/dist/sdk/context.d.ts +6 -0
  13. package/dist/sdk/context.js +11 -0
  14. package/dist/sdk/data-api.d.ts +21 -0
  15. package/dist/sdk/data-api.js +283 -0
  16. package/dist/sdk/data-cloud-api.d.ts +10 -0
  17. package/dist/sdk/data-cloud-api.js +59 -0
  18. package/dist/sdk/invocation-event.d.ts +8 -0
  19. package/dist/sdk/invocation-event.js +12 -0
  20. package/dist/sdk/logger.d.ts +10 -0
  21. package/dist/sdk/logger.js +31 -0
  22. package/dist/sdk/org.d.ts +14 -0
  23. package/dist/sdk/org.js +45 -0
  24. package/dist/sdk/sub-request.d.ts +32 -0
  25. package/dist/sdk/sub-request.js +77 -0
  26. package/dist/sdk/unit-of-work.d.ts +22 -0
  27. package/dist/sdk/unit-of-work.js +47 -0
  28. package/dist/sdk/user.d.ts +6 -0
  29. package/dist/sdk/user.js +10 -0
  30. package/dist/utils/addon-config.d.ts +7 -0
  31. package/dist/utils/addon-config.js +39 -0
  32. package/dist/utils/base-logger.d.ts +3 -0
  33. package/dist/utils/base-logger.js +27 -0
  34. package/dist/utils/create-connections.d.ts +7 -0
  35. package/dist/utils/create-connections.js +15 -0
  36. package/dist/utils/maps.d.ts +1 -0
  37. package/dist/utils/maps.js +18 -0
  38. package/dist/utils/request.d.ts +7 -0
  39. package/dist/utils/request.js +20 -0
  40. package/package.json +86 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+
8
+ ## [0.1.0-ea] - 2024-08-12
9
+
10
+ - Initial
11
+
12
+ ## [1.0.0-ea] - 2025-6-05
13
+ - Update CODEOWNERS
14
+ - Updated `getAuthorization` to use the correct API URL.
15
+ - Rename `getConnection(name: string)` -> `getAuthorization(developerName: string, attachmentNameOrColorUrl = "HEROKU_APPLINK")`, accepting a new attachmentNameOrColorOrUrl to use a specific Applink addon's config.
16
+ - Remove node-fetch in favor of native fetch, add `HTTPResponseError`
package/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, Salesforce.com, Inc.
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without modification,
7
+ are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice, this
13
+ list of conditions and the following disclaimer in the documentation and/or other
14
+ materials provided with the distribution.
15
+
16
+ 3. Neither the name of Salesforce.com nor the names of its contributors may be used
17
+ to endorse or promote products derived from this software without specific prior
18
+ written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Node.js SDK for Heroku AppLink
2
+
3
+ Use the Node.js SDK for Heroku AppLink to develop [Heroku AppLink](https://devcenter.heroku.com/articles/applink) managed apps that interact with Salesforce and Data Cloud orgs.
4
+
5
+ Node.js SDK for Heroku AppLink provides:
6
+ - Utilities to parse requests from Salesforce Heroku-type [External Services](https://help.salesforce.com/s/articleView?id=sf.external_services.htm&type=5) and Data Cloud [Data Action Targets](https://help.salesforce.com/s/articleView?id=sf.c360_a_create_a_data_action_target_of_webhook_type.htm&type=5)
7
+ hydrating SDK objects that response the request: [InvocationEvent](docs/interfaces/InvocationEvent.md) and [Context](docs/interfaces/Context.md).
8
+ - Objects that encapsulate the requesting Salesforce or Data Cloud [Org](docs/interfaces/Org.md) and [User](docs/interfaces/User.md).
9
+ - Utility objects that wrap Salesforce and Data Cloud APIs to perform simple CRUD operations and complex record manipulations.
10
+
11
+ And much more!
12
+
13
+ ## Documentation
14
+ For more information, see [API docs](docs/README.md).
15
+
16
+ ## Example
17
+ Example use of the Node.js SDK for Heroku AppLink provided as part of the [Heroku AppLink CLI Plugin's](https://github.com/heroku/heroku-cli-plugin-applnk) `applink:project` template:
18
+ ```javascript
19
+ fastify.get('/accounts', async function (request, reply) {
20
+ const {event, context, logger} = request.sdk;
21
+
22
+ logger.info(`GET /accounts: ${JSON.stringify(event.data || {})}`);
23
+
24
+ const query = 'SELECT Id, Name FROM Account';
25
+
26
+ if (process.env.SALESFORCE_ORG_NAME) {
27
+ // If an org reference is set, query Accounts in that org
28
+ const orgName = process.env.SALESFORCE_ORG_NAME;
29
+ const applinkAddon = request.sdk.addons.applink;
30
+
31
+ logger.info(`Getting org '${orgName}' connection from Heroku Applink add-on...`);
32
+ const anotherOrg = await applinkAddon.getAuthorization(orgName);
33
+
34
+ logger.info(`Querying org '${orgName}' (${anotherOrg.id}) Accounts...`);
35
+ try {
36
+ const result = await anotherOrg.dataApi.query(query);
37
+ const accounts = result.records.map(rec => rec.fields);
38
+ logger.info(`For org '${orgName}' (${anotherOrg.id}), found ${accounts.length} Accounts`);
39
+ } catch (err) {
40
+ logger.error(err.message);
41
+ }
42
+ }
43
+
44
+ // Query invoking org's Accounts
45
+ const org = context.org;
46
+ logger.info(`Querying invoking org (${org.id}) Accounts...`);
47
+ const result = await org.dataApi.query(query);
48
+ const accounts = result.records.map(rec => rec.fields);
49
+ logger.info(`For invoking org (${org.id}), found the following Accounts: ${JSON.stringify(accounts || {})}`);
50
+ return accounts;
51
+ });
52
+ ```
53
+
54
+ ## Configuration
55
+ * `HEROKU_APPLINK_ADDON_NAME` - For development, configures the SDK to use a different addon name in place of "HEROKU_APPLINK". Used when fetching config vars.
56
+
57
+ ## Testing
58
+ ### Dependency
59
+ You will need java installed to run wiremock.
60
+
61
+ ### Running tests
62
+ To test with coverage run `yarn test:coverage`
package/SECURITY.md ADDED
@@ -0,0 +1,7 @@
1
+ ## Security
2
+
3
+ Please report any security issue to [security@salesforce.com](mailto:security@salesforce.com)
4
+ as soon as it is discovered. This library limits its runtime dependencies in
5
+ order to reduce the total cost of ownership as much as can be, but all consumers
6
+ should remain vigilant and have their security stakeholders review all third-party
7
+ products (3PP) like this one and their dependencies.
@@ -0,0 +1,24 @@
1
+ Copyright 2024 [salesforce.com](https://app.salesforceiq.com/r?target=5aa041084cedfd007a609a0c&t=AFwhZf0GOvrkITUEGk_9Frn8rtTOQ_JcR3V1bPtHyKCQMx9cWuf92b6O6w9dz1LpYaK8lPgjNZvweaqC9W9fv2K3zv4BJWd_VftkdaL13QYpqzPyITBhy9poAZwbKc3bge5ItzFU2Lwc), inc. All rights reserved.
2
+
3
+ These Terms of Use are between **You** (being the Customer that purchased the Services) and [salesforce.com](salesforce.com), inc. or its Affiliate (“**SFDC**”) that entered into an agreement with You that governs Your purchase of the Services (the “**Agreement**”). Capitalized terms not defined herein shall have the meaning defined in the Agreement.
4
+
5
+ These Terms of Use were last updated on October 1, 2024, and constitute a legally binding agreement between You and SFDC effective upon Your first download, installation or use of the Software Development Kit (SDK), whichever is earliest. If You do not have authority to bind the Customer that entered into the Agreement or You do not agree to these Terms of Use, do not install or use the SDK.
6
+
7
+ SFDC grants You a limited non-exclusive, non­transferable, non-sublicensable, revocable license to use the Services in a manner consistent with the Documentation, and subject to the terms of the Agreement.
8
+
9
+ Use, modification, reproduction, and distribution of components of the SDK are licensed under an open source software license and are governed by the terms of that open source software license and not the Agreement. Some of the software required by or included in the SDK may be offered under other open source licenses. Open source licenses constitute separate written agreements.
10
+
11
+ Subject to the limited rights expressly granted hereunder, SFDC reserves all rights, title and interest in and to all intellectual property subsisting in the SDK as provided to You. No rights are granted to You hereunder other than as expressly set forth herein. Users residing in countries on the United States Office of Foreign Assets Control sanction list, or which are otherwise subject to a US export embargo, may not use the SDK.
12
+
13
+ The SDK is not part of the Services. Implementation of the SDK may require development work for which You are responsible. The SDK may contain bugs, errors and incompatibilities with Your configuration of the Services and is made available on an AS IS basis without support, updates, or service level commitments.
14
+
15
+ SFDC reserves the right at any time to modify, suspend or discontinue, the SDK (or any part thereof) with or without notice. You agree that SFDC shall not be liable to You or to any third party for any modification, suspension or discontinuance.
16
+
17
+ You agree to defend SFDC against any claim, demand, suit or proceeding made or brought against SFDC by a third party arising out of or accruing from (a) your use of the SDK, (b) any application you develop on the SDK that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with the Agreement, each to the extent allowable by applicable law (each a “Claim Against SFDC”), and will indemnify SFDC from any damages, attorney fees and costs finally awarded against SFDC as a result of, or for any amounts paid by SFDC under a settlement approved by You in writing of, a Claim Against SFDC, provided SFDC (a) promptly gives You written notice of the Claim Against SFDC, (b) gives You sole control of the defense and settlement of the Claim Against SFDC (except that You may not settle any Claim Against SFDC unless it unconditionally releases SFDC of all liability), and (c) gives You all reasonable assistance, at Your expense.
18
+
19
+ WITHOUT LIMITING THE GENERALITY OF THE FOREGOING, THE SDK IS NOT SUPPORTED, AND IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. IN NO EVENT SHALL SFDC HAVE ANY LIABILITY FOR ANY DAMAGES, INCLUDING BUT NOT LIMITED TO, DIRECT, INDIRECT, SPECIAL, INCIDENTAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES, OR DAMAGES BASED ON LOST PROFITS, DATA OR USE, IN CONNECTION WITH THE SDK, HOWEVER CAUSED AND WHETHER IN CONTRACT, TORT OR UNDER ANY OTHER THEORY OF LIABILITY, WHETHER OR NOT YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
20
+
21
+ These Terms of Use shall be governed exclusively by the internal laws of the State of California, without regard to its conflicts of laws rules. Each party hereby consents to the exclusive jurisdiction of the state and federal courts located in San Francisco County, California to adjudicate any dispute arising out of or relating to these Terms of Use. Except as expressly stated in these Terms of Use, these Terms of Use constitute the entire agreement between the parties, and supersede all prior and contemporaneous agreements, proposals or representations, written or oral, concerning their subject matter. No modification, amendment, or waiver of any provision of these Terms of Use shall be effective unless it is by an update to these Terms of Use that SFDC makes available on this website, or is in writing and signed by the party against whom the modification, amendment or waiver is to be asserted.
22
+
23
+ #### Data Privacy:
24
+ SFDC may collect, process and store device, system, and other information related to Your use of the SDK. This includes, but is not limited to, IP address, user metrics, and other data (“**Usage Data**”). SFDC may use Usage Data for analytics, product development and marketing purposes. You acknowledge that files generated in conjunction with the SDK may contain sensitive or confidential data, and You are solely responsible for anonymizing and protecting such data.
@@ -0,0 +1,2 @@
1
+ import { Org } from "../index";
2
+ export declare function getAuthorization(developerName: string, attachmentNameOrColorOrUrl?: string): Promise<Org>;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getAuthorization = getAuthorization;
4
+ const request_1 = require("../utils/request");
5
+ const org_1 = require("../sdk/org");
6
+ const addon_config_1 = require("../utils/addon-config");
7
+ const HTTP_REQUEST = new request_1.HttpRequestUtil();
8
+ async function getAuthorization(developerName, attachmentNameOrColorOrUrl = process.env.HEROKU_APPLINK_ADDON_NAME ||
9
+ "HEROKU_APPLINK") {
10
+ if (!developerName) {
11
+ throw Error(`Developer name not provided`);
12
+ }
13
+ let resolveConfigByUrl = false;
14
+ try {
15
+ new URL(attachmentNameOrColorOrUrl);
16
+ resolveConfigByUrl = true;
17
+ }
18
+ catch {
19
+ resolveConfigByUrl = false;
20
+ }
21
+ const config = resolveConfigByUrl
22
+ ? (0, addon_config_1.resolveAddonConfigByUrl)(attachmentNameOrColorOrUrl)
23
+ : (0, addon_config_1.resolveAddonConfigByAttachmentOrColor)(attachmentNameOrColorOrUrl);
24
+ const authUrl = `${config.apiUrl}/authorizations/${developerName}`;
25
+ const opts = {
26
+ method: "GET",
27
+ headers: {
28
+ Authorization: `Bearer ${config.token}`,
29
+ "Content-Type": "application/json",
30
+ },
31
+ retry: {
32
+ limit: 1,
33
+ },
34
+ };
35
+ let response;
36
+ try {
37
+ response = await HTTP_REQUEST.request(authUrl, opts);
38
+ }
39
+ catch (err) {
40
+ if (err instanceof request_1.HTTPResponseError) {
41
+ let errorResponse;
42
+ try {
43
+ errorResponse = await err.response.json();
44
+ }
45
+ catch (jsonError) {
46
+ }
47
+ if (errorResponse?.title && errorResponse?.detail) {
48
+ throw new Error(`${errorResponse.title} - ${errorResponse.detail}`);
49
+ }
50
+ }
51
+ throw new Error(`Unable to get connection ${developerName}: ${err.message}`);
52
+ }
53
+ return new org_1.OrgImpl(response.org.user_auth.access_token, response.org.api_version, null, response.org.id, response.org.instance_url, response.org.user_auth.user_id, response.org.user_auth.username, response.org.type);
54
+ }
@@ -0,0 +1,268 @@
1
+ import { QueryOperation } from "jsforce/lib/api/bulk";
2
+ import { getAuthorization } from "./add-ons/heroku-applink.js";
3
+ import { HTTPResponseError } from "./utils/request.js";
4
+ export declare function init(): {
5
+ addons: {
6
+ applink: {
7
+ getAuthorization: typeof getAuthorization;
8
+ };
9
+ };
10
+ dataCloud: {
11
+ parseDataActionEvent: typeof parseDataActionEvent;
12
+ };
13
+ salesforce: {
14
+ parseRequest: typeof parseRequest;
15
+ };
16
+ };
17
+ export declare function parseRequest(headers: any, body: any, log: any): {
18
+ event: InvocationEvent<any>;
19
+ context: Context;
20
+ logger: Logger;
21
+ };
22
+ export declare function parseDataActionEvent(payload: any): DataCloudActionEvent;
23
+ export { HTTPResponseError };
24
+ export interface InvocationEvent<A> {
25
+ readonly data: A;
26
+ readonly dataContentType?: string;
27
+ readonly id: string;
28
+ readonly time?: string;
29
+ }
30
+ export interface Context {
31
+ readonly id: string;
32
+ readonly org?: Org;
33
+ }
34
+ export interface Org {
35
+ readonly apiVersion: string;
36
+ readonly dataApi: DataApi;
37
+ readonly dataCloudApi?: DataCloudApi;
38
+ readonly domainUrl: string;
39
+ readonly id: string;
40
+ readonly namespace: string;
41
+ readonly user: User;
42
+ request(fullUrlOrUrlPart: string, opts: any, json: boolean): any;
43
+ }
44
+ export interface RecordQueryResult {
45
+ readonly done: boolean;
46
+ readonly totalSize: number;
47
+ readonly records: QueriedRecord[];
48
+ readonly nextRecordsUrl?: string;
49
+ }
50
+ export type Record = {
51
+ readonly type: string;
52
+ readonly fields: {
53
+ [key: string]: unknown;
54
+ };
55
+ readonly binaryFields?: {
56
+ [key: string]: Buffer;
57
+ };
58
+ };
59
+ export type QueriedRecord = Record & {
60
+ readonly subQueryResults: {
61
+ [sObjectName: string]: RecordQueryResult;
62
+ };
63
+ };
64
+ export interface RecordModificationResult {
65
+ readonly id: string;
66
+ }
67
+ export interface ReferenceId {
68
+ toString(): string;
69
+ toApiString(): string;
70
+ }
71
+ export type RecordForCreate = {
72
+ type: string;
73
+ fields: {
74
+ [key: string]: unknown;
75
+ };
76
+ binaryFields?: {
77
+ [key: string]: Buffer;
78
+ };
79
+ };
80
+ export type RecordForUpdate = {
81
+ type: string;
82
+ fields: {
83
+ id: string;
84
+ [key: string]: unknown;
85
+ };
86
+ binaryFields?: {
87
+ [key: string]: Buffer;
88
+ };
89
+ };
90
+ export interface UnitOfWork {
91
+ registerCreate(record: RecordForCreate): ReferenceId;
92
+ registerUpdate(record: RecordForUpdate): ReferenceId;
93
+ registerDelete(type: string, id: string): ReferenceId;
94
+ }
95
+ export interface DataApi {
96
+ readonly accessToken: string;
97
+ query(soql: string): Promise<RecordQueryResult>;
98
+ queryMore(recordQueryResult: RecordQueryResult): Promise<RecordQueryResult>;
99
+ create(record: RecordForCreate): Promise<RecordModificationResult>;
100
+ update(update: RecordForUpdate): Promise<RecordModificationResult>;
101
+ delete(type: string, id: string): Promise<RecordModificationResult>;
102
+ newUnitOfWork(): UnitOfWork;
103
+ commitUnitOfWork(unitOfWork: UnitOfWork): Promise<Map<ReferenceId, RecordModificationResult>>;
104
+ }
105
+ export interface User {
106
+ readonly id: string;
107
+ readonly username: string;
108
+ }
109
+ export interface Logger {
110
+ error(message: string): void;
111
+ warn(message: string): void;
112
+ info(message: string): void;
113
+ debug(message: string): void;
114
+ trace(message: string): void;
115
+ }
116
+ export interface BulkApi {
117
+ abort(jobReference: IngestJobReference | QueryJobReference): Promise<void>;
118
+ createDataTableBuilder(columns: [string, ...string[]]): DataTableBuilder;
119
+ delete(jobReference: IngestJobReference | QueryJobReference): Promise<void>;
120
+ formatNullValue(): string;
121
+ formatDate(value: Date): string;
122
+ formatDateTime(value: Date): string;
123
+ getFailedResults(jobReference: IngestJobReference): Promise<DataTable>;
124
+ getInfo(jobReference: IngestJobReference | QueryJobReference): Promise<IngestJobInfo | QueryJobInfo>;
125
+ getMoreQueryResults(queryJobResults: QueryJobResults, getQueryJobResultsOptions?: GetQueryJobResultsOptions): Promise<QueryJobResults>;
126
+ getQueryResults(jobReference: QueryJobReference, getQueryJobResultsOptions?: GetQueryJobResultsOptions): Promise<QueryJobResults>;
127
+ getSuccessfulResults(jobReference: IngestJobReference): Promise<DataTable>;
128
+ getUnprocessedRecords(jobReference: IngestJobReference): Promise<DataTable>;
129
+ ingest(ingestJobOptions: IngestJobOptions): Promise<Array<IngestJobReference | IngestJobFailure>>;
130
+ query(options: QueryJobOptions): Promise<QueryJobReference>;
131
+ splitDataTable(dataTable: DataTable): DataTable[];
132
+ }
133
+ export interface IngestJobOptions {
134
+ dataTable: DataTable;
135
+ object: string;
136
+ operation: IngestJobOperation;
137
+ externalIdFieldName?: string;
138
+ assignmentRuleId?: string;
139
+ }
140
+ export interface QueryJobOptions {
141
+ soql: string;
142
+ operation?: QueryJobOperation;
143
+ }
144
+ export interface IngestJobReference {
145
+ id: string;
146
+ type: "ingestJob";
147
+ }
148
+ export interface QueryJobReference {
149
+ id: string;
150
+ type: "queryJob";
151
+ }
152
+ export interface GetQueryJobResultsOptions {
153
+ maxRecords: number;
154
+ }
155
+ interface JobInfo {
156
+ apexProcessingTime?: number;
157
+ apiActiveProcessingTime?: number;
158
+ apiVersion: number;
159
+ columnDelimiter: "COMMA";
160
+ concurrencyMode: "Parallel";
161
+ contentType: "CSV";
162
+ contentUrl?: string;
163
+ createdById: string;
164
+ createdDate: string;
165
+ id: string;
166
+ jobType: "V2Ingest" | "V2Query";
167
+ lineEnding: "LF";
168
+ object: string;
169
+ operation: IngestJobOperation | QueryOperation;
170
+ retries?: number;
171
+ state: IngestJobState | QueryJobState;
172
+ systemModstamp: string;
173
+ totalProcessingTime?: number;
174
+ assignmentRuleId?: string;
175
+ externalIdFieldName?: string;
176
+ numberRecordsFailed?: number;
177
+ numberRecordsProcessed?: number;
178
+ }
179
+ export interface IngestJobInfo extends JobInfo {
180
+ jobType: "V2Ingest";
181
+ operation: IngestJobOperation;
182
+ state: IngestJobState;
183
+ assignmentRuleId?: string;
184
+ externalIdFieldName?: string;
185
+ numberRecordsFailed?: number;
186
+ numberRecordsProcessed?: number;
187
+ }
188
+ export interface QueryJobInfo extends JobInfo {
189
+ jobType: "V2Query";
190
+ operation: QueryJobOperation;
191
+ state: QueryJobState;
192
+ }
193
+ export type IngestJobState = "Open" | "UploadComplete" | "InProgress" | "Aborted" | "JobComplete" | "Failed";
194
+ export type QueryJobState = "UploadComplete" | "InProgress" | "Aborted" | "JobComplete" | "Failed";
195
+ export type IngestJobOperation = "insert" | "delete" | "hardDelete" | "update" | "upsert";
196
+ export type QueryJobOperation = "query" | "queryAll";
197
+ export interface QueryJobResults {
198
+ readonly dataTable: DataTable;
199
+ readonly done: boolean;
200
+ readonly locator?: string;
201
+ readonly numberOfRecords: number;
202
+ readonly jobReference: QueryJobReference;
203
+ }
204
+ export interface DataTable extends Array<Map<string, string>> {
205
+ columns: [string, ...string[]];
206
+ }
207
+ export type DataTableFieldValueExtractor<T> = (data: T, columnName: string) => string;
208
+ export interface DataTableBuilder {
209
+ addRow(row: string[] | Map<string, string>): DataTableBuilder;
210
+ addRow<T>(value: T, fieldValueExtractor: DataTableFieldValueExtractor<T>): any;
211
+ addRows(rows: Array<string[] | Map<string, string>>): DataTableBuilder;
212
+ addRows<T>(values: Array<T>, fieldValueExtractor: DataTableFieldValueExtractor<T>): DataTableBuilder;
213
+ build(): DataTable;
214
+ }
215
+ export interface IngestJobFailure {
216
+ error: BulkApiError;
217
+ unprocessedRecords: DataTable;
218
+ jobReference?: IngestJobReference;
219
+ }
220
+ export interface BulkApiError extends Error {
221
+ errorCode: string;
222
+ }
223
+ export interface DataCloudActionEventSchema {
224
+ schemaId: string;
225
+ schema: string;
226
+ }
227
+ export interface DataObjectDataChgEvent {
228
+ ActionDeveloperName: string;
229
+ EventCreationDateTime: string;
230
+ EventPrompt: string;
231
+ EventPublishDateTime: string;
232
+ EventSchemaVersion: string;
233
+ EventType: string;
234
+ EventUuid: string;
235
+ Offset: string;
236
+ PayloadCurrentValue: string;
237
+ PayloadPrevValue: string;
238
+ PayloadMetadata: string;
239
+ ReplayId: string;
240
+ SourceObjectDeveloperName: string;
241
+ }
242
+ export interface DataCloudActionEvent {
243
+ creationDateTime: string;
244
+ count: number;
245
+ schemas: DataCloudActionEventSchema[];
246
+ events: DataObjectDataChgEvent[];
247
+ }
248
+ export interface DataCloudQuery {
249
+ sql: string;
250
+ }
251
+ export interface DataCloudQueryResponse {
252
+ data: Array<any>;
253
+ startTime: string;
254
+ endTime: string;
255
+ rowCount: number;
256
+ queryId: string;
257
+ nextBatchId: string;
258
+ done: boolean;
259
+ metadata: any;
260
+ }
261
+ export interface DataCloudUpsertResponse {
262
+ accepted: boolean;
263
+ }
264
+ export interface DataCloudApi {
265
+ query(sql: DataCloudQuery): Promise<DataCloudQueryResponse>;
266
+ queryNextBatch(nextBatchId: string): Promise<DataCloudQueryResponse>;
267
+ upsert(name: string, objectName: string, data: any): Promise<DataCloudUpsertResponse>;
268
+ }
package/dist/index.js ADDED
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HTTPResponseError = void 0;
4
+ exports.init = init;
5
+ exports.parseRequest = parseRequest;
6
+ exports.parseDataActionEvent = parseDataActionEvent;
7
+ const context_js_1 = require("./sdk/context.js");
8
+ const invocation_event_js_1 = require("./sdk/invocation-event.js");
9
+ const logger_js_1 = require("./sdk/logger.js");
10
+ const heroku_applink_js_1 = require("./add-ons/heroku-applink.js");
11
+ const request_js_1 = require("./utils/request.js");
12
+ Object.defineProperty(exports, "HTTPResponseError", { enumerable: true, get: function () { return request_js_1.HTTPResponseError; } });
13
+ const CONTENT_TYPE_HEADER = "Content-Type";
14
+ const X_CLIENT_CONTEXT_HEADER = "x-client-context";
15
+ function init() {
16
+ return {
17
+ addons: {
18
+ applink: {
19
+ getAuthorization: heroku_applink_js_1.getAuthorization,
20
+ },
21
+ },
22
+ dataCloud: {
23
+ parseDataActionEvent,
24
+ },
25
+ salesforce: {
26
+ parseRequest,
27
+ },
28
+ };
29
+ }
30
+ function parseRequest(headers, body, log) {
31
+ if (!headers) {
32
+ throw Error(`Request headers not provided`);
33
+ }
34
+ const encodedClientContext = headers[X_CLIENT_CONTEXT_HEADER];
35
+ if (!encodedClientContext) {
36
+ throw Error(`Required ${X_CLIENT_CONTEXT_HEADER} header not found`);
37
+ }
38
+ const clientContext = JSON.parse(Buffer.from(encodedClientContext, "base64").toString("utf8"));
39
+ if (!clientContext.userContext) {
40
+ throw Error(`Required UserContext not found on ${X_CLIENT_CONTEXT_HEADER}`);
41
+ }
42
+ const context = new context_js_1.ContextImpl(clientContext.accessToken, clientContext.apiVersion, clientContext.requestId, clientContext.namespace, clientContext.orgId, clientContext.orgDomainUrl, clientContext.userContext.userId, clientContext.userContext.username, "SalesforceOrg");
43
+ const invocationEvent = new invocation_event_js_1.InvocationEventImpl(body || "", headers[CONTENT_TYPE_HEADER] || "application/json", context.id, new Date().getTime() + "");
44
+ return {
45
+ event: invocationEvent,
46
+ context,
47
+ logger: log || new logger_js_1.LoggerImpl(context.id),
48
+ };
49
+ }
50
+ function parseDataActionEvent(payload) {
51
+ return payload;
52
+ }
@@ -0,0 +1,3 @@
1
+ import { BulkApi } from "../index";
2
+ import { CreateConnectionOptions } from "../utils/create-connections";
3
+ export declare function createBulkApi(clientOptions: CreateConnectionOptions): BulkApi;