conductor-node 9.8.0 → 10.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 +39 -38
- package/dist/package.json +6 -16
- package/dist/src/Client.d.ts +6 -46
- package/dist/src/Client.js +23 -64
- package/dist/src/integrations/BaseIntegration.d.ts +6 -6
- package/dist/src/integrations/BaseIntegration.js +6 -7
- package/dist/src/integrations/qbd/QbdIntegration.js +1 -4
- package/dist/src/interceptors/errorHandling.d.ts +2 -0
- package/dist/src/interceptors/errorHandling.js +45 -0
- package/dist/src/interceptors/logging.d.ts +7 -0
- package/dist/src/interceptors/logging.js +61 -0
- package/dist/src/resources/BaseResource.d.ts +6 -0
- package/dist/src/resources/BaseResource.js +9 -0
- package/dist/src/resources/IntegrationConnectionsResource.d.ts +59 -0
- package/dist/src/resources/IntegrationConnectionsResource.js +66 -0
- package/dist/src/utils/checkForUpdates.d.ts +1 -0
- package/dist/src/utils/{misc.js → checkForUpdates.js} +2 -9
- package/dist/src/utils/error.d.ts +21 -14
- package/dist/src/utils/error.js +18 -8
- package/dist/src/utils/{misc.d.ts → http.d.ts} +0 -1
- package/dist/src/utils/http.js +10 -0
- package/package.json +6 -16
- package/dist/src/graphql/__generated__/operationTypes.d.ts +0 -118
- package/dist/src/graphql/__generated__/operationTypes.js +0 -71
- package/dist/src/graphql/graphqlOperationWrapper.d.ts +0 -8
- package/dist/src/graphql/graphqlOperationWrapper.js +0 -108
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Execute _any_ read or write [QuickBooks Desktop API](https://developer.intuit.co
|
|
|
16
16
|
|
|
17
17
|
## Requirements
|
|
18
18
|
|
|
19
|
-
1. A Conductor API key
|
|
19
|
+
1. A Conductor API key.
|
|
20
20
|
2. A running version of QuickBooks Desktop connected to Conductor. See our [guide to connecting QuickBooks Desktop to Conductor](https://conductor-io.notion.site/QuickBooks-Desktop-integration-setup-86f6325f747d4447b95d27b59ad89ef5).
|
|
21
21
|
|
|
22
22
|
## Installation
|
|
@@ -34,7 +34,7 @@ import Conductor from "conductor-node";
|
|
|
34
34
|
const conductor = new Conductor("sk_test_...");
|
|
35
35
|
|
|
36
36
|
// Fetch all authorized integration-connections.
|
|
37
|
-
const qbdConnections = await conductor.
|
|
37
|
+
const qbdConnections = await conductor.integrationConnection.list();
|
|
38
38
|
|
|
39
39
|
// Execute any QBD API against your QBD connection id.
|
|
40
40
|
const newAccount = await conductor.qbd.account.add(qbdConnections[0].id, {
|
|
@@ -46,12 +46,20 @@ const newAccount = await conductor.qbd.account.add(qbdConnections[0].id, {
|
|
|
46
46
|
|
|
47
47
|
## APIs
|
|
48
48
|
|
|
49
|
-
### `
|
|
49
|
+
### `integrationConnections.list()`
|
|
50
|
+
|
|
51
|
+
Returns a of list all integration-connections associated with your Conductor account.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const qbdConnections = await conductor.integrationConnections.list();
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### `integrationConnections.create(input: CreateIntegrationConnectionInput)`
|
|
50
58
|
|
|
51
59
|
Creates a new integration-connection.
|
|
52
60
|
|
|
53
61
|
```ts
|
|
54
|
-
const newQbdConnection = await conductor.
|
|
62
|
+
const newQbdConnection = await conductor.integrationConnections.create({
|
|
55
63
|
// The identifier of the third-party platform to integrate.
|
|
56
64
|
integrationKey: "quickbooks-desktop",
|
|
57
65
|
// Your end-user's unique ID in your product's database. Must be
|
|
@@ -76,49 +84,30 @@ The response looks like the following:
|
|
|
76
84
|
endUserSourceId: "1234-abcd",
|
|
77
85
|
endUserEmail: 'danny@constructionco.com',
|
|
78
86
|
endUserCompanyName: 'Construction Corp',
|
|
79
|
-
lastHeartbeatAt: null
|
|
80
87
|
}
|
|
81
88
|
```
|
|
82
89
|
|
|
83
|
-
### `
|
|
84
|
-
|
|
85
|
-
Executes any QuickBooks Desktop (QBD) API against a specific integration-connection id. See the official [QuickBooks Desktop API Reference](https://developer.intuit.com/app/developer/qbdesktop/docs/api-reference/qbdesktop) for a complete list of available APIs.
|
|
86
|
-
|
|
87
|
-
```ts
|
|
88
|
-
const newAccount = await conductor.qbd.account.add(qbdConnectionId, {
|
|
89
|
-
Name: "Test Account",
|
|
90
|
-
AccountType: "Bank",
|
|
91
|
-
OpenBalance: "100",
|
|
92
|
-
});
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### `getIntegrationConnections()`
|
|
96
|
-
|
|
97
|
-
Fetches all authorized integration-connections.
|
|
98
|
-
|
|
99
|
-
```ts
|
|
100
|
-
const qbdConnections = await conductor.getIntegrationConnections();
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### `getIntegrationConnection(id: string)`
|
|
90
|
+
### `integrationConnections.retrieve(id: string)`
|
|
104
91
|
|
|
105
|
-
|
|
92
|
+
Retrieves the specified integration-connection.
|
|
106
93
|
|
|
107
94
|
```ts
|
|
108
|
-
const qbdConnection = await conductor.
|
|
95
|
+
const qbdConnection = await conductor.integrationConnections.retrieve(
|
|
96
|
+
qbdConnectionId
|
|
97
|
+
);
|
|
109
98
|
```
|
|
110
99
|
|
|
111
|
-
### `
|
|
100
|
+
### `integrationConnections.ping(id: string)`
|
|
112
101
|
|
|
113
102
|
Checks whether the specified integration-connection can connect and process requests end-to-end.
|
|
114
103
|
|
|
115
|
-
If the connection fails, the error we encountered will be thrown as a [`ConductorError`](#error-handling). This information is useful for showing a "connection status" indicator in your app. If an error occurs, we recommend displaying the property `error.endUserMessage` to your end-user in your app's UI.
|
|
104
|
+
If the connection fails, the error we encountered will be thrown as a [`ConductorError`](#error-handling) (like any request). This information is useful for showing a "connection status" indicator in your app. If an error occurs, we recommend displaying the property `error.endUserMessage` to your end-user in your app's UI.
|
|
116
105
|
|
|
117
106
|
Using `async`/`await`:
|
|
118
107
|
|
|
119
108
|
```ts
|
|
120
109
|
try {
|
|
121
|
-
await conductor.
|
|
110
|
+
await conductor.integrationConnections.ping(qbdConnectionId);
|
|
122
111
|
} catch (error) {
|
|
123
112
|
if (error instanceof ConductorError) {
|
|
124
113
|
// Update your app's UI to display `error.endUserMessage`.
|
|
@@ -130,7 +119,7 @@ try {
|
|
|
130
119
|
Or in the form of a rejected promise:
|
|
131
120
|
|
|
132
121
|
```ts
|
|
133
|
-
conductor.
|
|
122
|
+
conductor.integrationConnections.ping(qbdConnectionId).catch((error) => {
|
|
134
123
|
if (error instanceof ConductorError) {
|
|
135
124
|
// Update your app's UI to display `error.endUserMessage`.
|
|
136
125
|
}
|
|
@@ -138,6 +127,18 @@ conductor.pingIntegrationConnection(qbdConnectionId).catch((error) => {
|
|
|
138
127
|
});
|
|
139
128
|
```
|
|
140
129
|
|
|
130
|
+
### `qbd.*`
|
|
131
|
+
|
|
132
|
+
Executes any QuickBooks Desktop (QBD) API against a specific integration-connection id. See the official [QuickBooks Desktop API Reference](https://developer.intuit.com/app/developer/qbdesktop/docs/api-reference/qbdesktop) for a complete list of available APIs.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const newAccount = await conductor.qbd.account.add(qbdConnectionId, {
|
|
136
|
+
Name: "Test Account",
|
|
137
|
+
AccountType: "Bank",
|
|
138
|
+
OpenBalance: "100",
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
141
142
|
## TypeScript
|
|
142
143
|
|
|
143
144
|
Access the entire QuickBooks Desktop API through TypeScript. The `qbd.*` APIs are fully typed with inline documentation and will autocomplete in your editor.
|
|
@@ -163,11 +164,11 @@ All errors thrown by the Conductor API are instances of `ConductorError` or its
|
|
|
163
164
|
| Property | Type | Description |
|
|
164
165
|
| ----------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
165
166
|
| `type` | `string` | Categorizes the error. See [Error Types](#error-types) below.<br><br>This value is the same as the subclass name. E.g., `"ConductorIntegrationError"` or `"ConductorInvalidRequestError"`. |
|
|
166
|
-
| `code` | `string` | The unique error code from Conductor, which is useful for adding special handling for specific errors. E.g., `"
|
|
167
|
+
| `code` | `string` | The unique error code from Conductor, which is useful for adding special handling for specific errors. E.g., `"RESOURCE_MISSING"`, `"API_KEY_INVALID"`, or `"QBD_REQUEST_ERROR"`.<br><br>In contrast, `type` is more general and categorizes the error. |
|
|
168
|
+
| `httpStatusCode` | `number` or `undefined` | The HTTP status code of the response that included the error. You probably won't need this. |
|
|
167
169
|
| `message` | `string` | The developer-friendly error message for your logs. |
|
|
168
170
|
| `endUserMessage` | `string` | The end-user-friendly error message to display in your app's UI to your end-users.<br><br>This value exists for _every_ error. E.g., for a QBD connection error, it might recommend the end-user to check that their QuickBooks Desktop is open and that they're logged in. But if a Conductor API key is expired, e.g., this message will just say "An internal server error occurred. Please try again later.". |
|
|
169
171
|
| `integrationCode` | `string` or `undefined` | The unique error code supplied by the third-party integration for errors returned by the integration (i.e., `ConductorIntegrationError`) or integration connector (i.e., `ConductorIntegrationConnectorError`). This is useful for adding special handling for specific errors from the third-party integration or connector.<br><br>The integration's corresponding error message for this code is in `error.message`.<br><br>The third-party integrations' error codes are not standardized, so you should not rely on this code to be the same across integrations. |
|
|
170
|
-
| `httpStatusCode` | `number` or `undefined` | The HTTP status code of the response that included the error. You probably won't need this. |
|
|
171
172
|
|
|
172
173
|
### Error Types
|
|
173
174
|
|
|
@@ -225,16 +226,16 @@ conductor.qbd.account
|
|
|
225
226
|
});
|
|
226
227
|
```
|
|
227
228
|
|
|
228
|
-
###
|
|
229
|
+
### Global Error Handling
|
|
229
230
|
|
|
230
|
-
It is unnecessary to wrap each API call individually, as demonstrated in the examples above. Instead, we suggest implementing a
|
|
231
|
+
It is unnecessary to wrap each API call individually, as demonstrated in the examples above. Instead, we suggest implementing a Global error handler for your server, such as [`app.use((error, ...) => { ... })` in Express](https://expressjs.com/en/guide/error-handling.html#writing-error-handlers) or [`formatError` in Apollo Server](https://apollographql.com/docs/apollo-server/data/errors/#for-client-responses). Within this handler, perform the following actions:
|
|
231
232
|
|
|
232
233
|
1. For any `ConductorError` instance, display the `error.endUserMessage` property to the end-user in your app's UI while logging the complete error object.
|
|
233
234
|
2. For all `ConductorError` instances, transmit the full error object to your error-tracking service (e.g., Sentry):
|
|
234
235
|
- Send a **warning** for instances of `ConductorIntegrationConnectionError`, which are not actionable by you and can only be resolved by the end-user; for example, failure to connect to QuickBooks Desktop on the end-user's computer.
|
|
235
236
|
- Send an **error** for all other `ConductorError` instances, such as an invalid API key.
|
|
236
237
|
|
|
237
|
-
For example, using an Express error handler:
|
|
238
|
+
For example, using an [Express error handler](https://expressjs.com/en/guide/error-handling.html#writing-error-handlers):
|
|
238
239
|
|
|
239
240
|
```ts
|
|
240
241
|
import * as Sentry from "@sentry/node";
|
|
@@ -260,7 +261,7 @@ app.use((error, req, res, next) => {
|
|
|
260
261
|
});
|
|
261
262
|
```
|
|
262
263
|
|
|
263
|
-
Or using Apollo Server's error handler:
|
|
264
|
+
Or using [Apollo Server's error handler](https://apollographql.com/docs/apollo-server/data/errors/#for-client-responses):
|
|
264
265
|
|
|
265
266
|
```ts
|
|
266
267
|
import { ApolloServer } from "@apollo/server";
|
package/dist/package.json
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conductor-node",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Easily integrate
|
|
5
|
-
"author": "Danny Nemer <hi@DannyNemer.com>",
|
|
3
|
+
"version": "10.0.0",
|
|
4
|
+
"description": "Easily integrate the entire QuickBooks Desktop API using fully-typed async TypeScript",
|
|
6
5
|
"license": "MIT",
|
|
7
6
|
"type": "commonjs",
|
|
8
7
|
"main": "dist/src/index.js",
|
|
@@ -13,10 +12,9 @@
|
|
|
13
12
|
],
|
|
14
13
|
"scripts": {
|
|
15
14
|
"prepack": "yarn --silent tsc && yarn --silent delete-compiled-dev-files && yarn --silent tsc-alias",
|
|
16
|
-
"delete-compiled-dev-files": "rm -rf `find ./dist -type d -name __tests__` ./dist/src/utils/
|
|
15
|
+
"delete-compiled-dev-files": "rm -rf `find ./dist -type d -name __tests__` ./dist/src/utils/test/*",
|
|
17
16
|
"postpack": "rm -rf dist",
|
|
18
17
|
"clean": "rm -rf dist package conductor-node-*.tgz tsconfig.tsbuildinfo",
|
|
19
|
-
"gen:graphql": "yarn graphql-codegen --config=./src/graphql/codegenConfig.ts",
|
|
20
18
|
"diff": "yarn pack && npm diff --diff=conductor-node@latest --diff=conductor-node-v$(node -p \"require('./package.json').version\").tgz && yarn clean",
|
|
21
19
|
"prepublishOnly": "yarn test",
|
|
22
20
|
"test": "yarn --cwd=../../ test ./packages/client",
|
|
@@ -26,25 +24,17 @@
|
|
|
26
24
|
"node": ">=16"
|
|
27
25
|
},
|
|
28
26
|
"dependencies": {
|
|
29
|
-
"
|
|
30
|
-
"graphql-request": "~5.1.0"
|
|
27
|
+
"axios": "^1.4.0"
|
|
31
28
|
},
|
|
32
29
|
"devDependencies": {
|
|
33
|
-
"
|
|
34
|
-
"@graphql-codegen/cli": "^3.2.1",
|
|
35
|
-
"@graphql-codegen/typescript-graphql-request": "^4.5.8",
|
|
36
|
-
"@graphql-codegen/typescript-operations": "^3.0.1",
|
|
30
|
+
"axios-mock-adapter": "^1.21.4",
|
|
37
31
|
"tsc-alias": "^1.7.0"
|
|
38
32
|
},
|
|
39
33
|
"keywords": [
|
|
40
|
-
"accounting",
|
|
41
|
-
"api",
|
|
42
34
|
"conductor",
|
|
43
35
|
"qbd",
|
|
44
36
|
"qbwc",
|
|
45
37
|
"qbxml",
|
|
46
|
-
"quickbooks desktop"
|
|
47
|
-
"sdk",
|
|
48
|
-
"typescript"
|
|
38
|
+
"quickbooks desktop"
|
|
49
39
|
]
|
|
50
40
|
}
|
package/dist/src/Client.d.ts
CHANGED
|
@@ -1,57 +1,17 @@
|
|
|
1
|
-
import type { GraphqlCreateIntegrationConnectionInput, GraphqlCreateIntegrationConnectionMutation, GraphqlGetIntegrationConnectionQuery, GraphqlGetIntegrationConnectionQueryVariables, GraphqlGetIntegrationConnectionsQuery, GraphqlPingIntegrationConnectionMutation, GraphqlPingIntegrationConnectionMutationVariables } from "./graphql/__generated__/operationTypes";
|
|
2
1
|
import QbdIntegration from "./integrations/qbd/QbdIntegration";
|
|
3
|
-
import
|
|
2
|
+
import IntegrationConnectionsResource from "./resources/IntegrationConnectionsResource";
|
|
3
|
+
import { getServerUrlForEnvironment } from "./utils/http";
|
|
4
4
|
export interface ClientOptions {
|
|
5
|
-
/** Logs each request and
|
|
5
|
+
/** Logs each request, response, and error. */
|
|
6
6
|
readonly verbose?: boolean;
|
|
7
7
|
readonly serverEnvironment?: Parameters<typeof getServerUrlForEnvironment>[0];
|
|
8
8
|
}
|
|
9
9
|
export default class Client {
|
|
10
|
+
readonly integrationConnections: IntegrationConnectionsResource;
|
|
10
11
|
/** QuickBooks Desktop integration. */
|
|
11
12
|
readonly qbd: QbdIntegration;
|
|
12
|
-
private readonly
|
|
13
|
-
private readonly graphqlOperations;
|
|
13
|
+
private readonly httpClient;
|
|
14
14
|
constructor(apiKey: string, { verbose, serverEnvironment }?: ClientOptions);
|
|
15
|
-
|
|
16
|
-
* Fetches all integration-connections associated with your Conductor account.
|
|
17
|
-
*
|
|
18
|
-
* @returns The integration-connections.
|
|
19
|
-
*/
|
|
20
|
-
getIntegrationConnections(): Promise<GraphqlGetIntegrationConnectionsQuery["integrationConnections"]>;
|
|
21
|
-
/**
|
|
22
|
-
* Fetches the specified integration-connection.
|
|
23
|
-
*
|
|
24
|
-
* @param integrationConnectionId The integration-connection ID.
|
|
25
|
-
* @returns The integration-connection.
|
|
26
|
-
*/
|
|
27
|
-
getIntegrationConnection(integrationConnectionId: GraphqlGetIntegrationConnectionQueryVariables["integrationConnectionId"]): Promise<GraphqlGetIntegrationConnectionQuery["integrationConnection"]>;
|
|
28
|
-
/**
|
|
29
|
-
* Creates a new integration-connection.
|
|
30
|
-
*
|
|
31
|
-
* @param input - The input object to create the integration-connection.
|
|
32
|
-
* @param input.integrationKey The identifier of the third-party platform to
|
|
33
|
-
* integrate.
|
|
34
|
-
* @param input.endUserSourceId Your end-user's unique ID in your product's
|
|
35
|
-
* database. Must be distinct from your other connections for the same
|
|
36
|
-
* integration.
|
|
37
|
-
* @param input.endUserEmail Your end-user's email address for identification
|
|
38
|
-
* only. No emails will be sent.
|
|
39
|
-
* @param input.endUserCompanyName Your end-user's company name that will be
|
|
40
|
-
* shown elsewhere in Conductor.
|
|
41
|
-
* @returns The newly created integration-connection.
|
|
42
|
-
*/
|
|
43
|
-
createIntegrationConnection(input: GraphqlCreateIntegrationConnectionInput & {
|
|
44
|
-
integrationKey: "quickbooks-desktop";
|
|
45
|
-
}): Promise<GraphqlCreateIntegrationConnectionMutation["createIntegrationConnection"]["integrationConnection"]>;
|
|
46
|
-
/**
|
|
47
|
-
* Checks whether the specified integration-connection can connect and process
|
|
48
|
-
* requests end-to-end.
|
|
49
|
-
*
|
|
50
|
-
* If the connection fails, the error we encountered will be thrown as a
|
|
51
|
-
* `ConductorError`. This information is useful for showing a "connection
|
|
52
|
-
* status" indicator in your app. If an error occurs, we recommend displaying
|
|
53
|
-
* the property `error.endUserMessage` to your end-user in your app's UI.
|
|
54
|
-
*/
|
|
55
|
-
pingIntegrationConnection(integrationConnectionId: GraphqlPingIntegrationConnectionMutationVariables["input"]["integrationConnectionId"]): Promise<GraphqlPingIntegrationConnectionMutation["pingIntegrationConnection"]>;
|
|
15
|
+
private createHttpClient;
|
|
56
16
|
private createHeaders;
|
|
57
17
|
}
|
package/dist/src/Client.js
CHANGED
|
@@ -4,76 +4,35 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const package_json_1 = __importDefault(require("./../package.json"));
|
|
7
|
-
const operationTypes_1 = require("./graphql/__generated__/operationTypes");
|
|
8
|
-
const graphqlOperationWrapper_1 = require("./graphql/graphqlOperationWrapper");
|
|
9
7
|
const QbdIntegration_1 = __importDefault(require("./integrations/qbd/QbdIntegration"));
|
|
10
|
-
const
|
|
11
|
-
const
|
|
8
|
+
const errorHandling_1 = require("./interceptors/errorHandling");
|
|
9
|
+
const logging_1 = require("./interceptors/logging");
|
|
10
|
+
const IntegrationConnectionsResource_1 = __importDefault(require("./resources/IntegrationConnectionsResource"));
|
|
11
|
+
const checkForUpdates_1 = require("./utils/checkForUpdates");
|
|
12
|
+
const http_1 = require("./utils/http");
|
|
13
|
+
const axios_1 = __importDefault(require("axios"));
|
|
12
14
|
class Client {
|
|
15
|
+
integrationConnections;
|
|
13
16
|
/** QuickBooks Desktop integration. */
|
|
14
17
|
qbd;
|
|
15
|
-
|
|
16
|
-
graphqlOperations;
|
|
18
|
+
httpClient;
|
|
17
19
|
constructor(apiKey, { verbose = false, serverEnvironment = "production" } = {}) {
|
|
18
|
-
(0,
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.qbd = new QbdIntegration_1.default(this.
|
|
20
|
+
(0, checkForUpdates_1.checkForUpdates)();
|
|
21
|
+
this.httpClient = this.createHttpClient(apiKey, verbose, serverEnvironment);
|
|
22
|
+
this.integrationConnections = new IntegrationConnectionsResource_1.default(this.httpClient);
|
|
23
|
+
this.qbd = new QbdIntegration_1.default(this.httpClient);
|
|
22
24
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
* Fetches the specified integration-connection.
|
|
35
|
-
*
|
|
36
|
-
* @param integrationConnectionId The integration-connection ID.
|
|
37
|
-
* @returns The integration-connection.
|
|
38
|
-
*/
|
|
39
|
-
async getIntegrationConnection(integrationConnectionId) {
|
|
40
|
-
return this.graphqlOperations
|
|
41
|
-
.getIntegrationConnection({ integrationConnectionId })
|
|
42
|
-
.then((result) => result.integrationConnection);
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Creates a new integration-connection.
|
|
46
|
-
*
|
|
47
|
-
* @param input - The input object to create the integration-connection.
|
|
48
|
-
* @param input.integrationKey The identifier of the third-party platform to
|
|
49
|
-
* integrate.
|
|
50
|
-
* @param input.endUserSourceId Your end-user's unique ID in your product's
|
|
51
|
-
* database. Must be distinct from your other connections for the same
|
|
52
|
-
* integration.
|
|
53
|
-
* @param input.endUserEmail Your end-user's email address for identification
|
|
54
|
-
* only. No emails will be sent.
|
|
55
|
-
* @param input.endUserCompanyName Your end-user's company name that will be
|
|
56
|
-
* shown elsewhere in Conductor.
|
|
57
|
-
* @returns The newly created integration-connection.
|
|
58
|
-
*/
|
|
59
|
-
async createIntegrationConnection(input) {
|
|
60
|
-
return this.graphqlOperations
|
|
61
|
-
.createIntegrationConnection({ input })
|
|
62
|
-
.then((result) => result.createIntegrationConnection.integrationConnection);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Checks whether the specified integration-connection can connect and process
|
|
66
|
-
* requests end-to-end.
|
|
67
|
-
*
|
|
68
|
-
* If the connection fails, the error we encountered will be thrown as a
|
|
69
|
-
* `ConductorError`. This information is useful for showing a "connection
|
|
70
|
-
* status" indicator in your app. If an error occurs, we recommend displaying
|
|
71
|
-
* the property `error.endUserMessage` to your end-user in your app's UI.
|
|
72
|
-
*/
|
|
73
|
-
async pingIntegrationConnection(integrationConnectionId) {
|
|
74
|
-
return this.graphqlOperations
|
|
75
|
-
.pingIntegrationConnection({ input: { integrationConnectionId } })
|
|
76
|
-
.then((result) => result.pingIntegrationConnection);
|
|
25
|
+
createHttpClient(apiKey, verbose, serverEnvironment) {
|
|
26
|
+
const httpClient = axios_1.default.create({
|
|
27
|
+
baseURL: `${(0, http_1.getServerUrlForEnvironment)(serverEnvironment)}/v1`,
|
|
28
|
+
headers: this.createHeaders(apiKey),
|
|
29
|
+
timeout: 0, // No timeout (default).
|
|
30
|
+
});
|
|
31
|
+
// Wrap errors in `ConductorError`.
|
|
32
|
+
(0, errorHandling_1.addErrorHandlingInterceptors)(httpClient);
|
|
33
|
+
// Must be the last interceptor for error logging to use the wrapped error.
|
|
34
|
+
(0, logging_1.addLoggingInterceptors)(httpClient, verbose);
|
|
35
|
+
return httpClient;
|
|
77
36
|
}
|
|
78
37
|
createHeaders(apiKey) {
|
|
79
38
|
return {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type {
|
|
3
|
-
export default class BaseIntegration {
|
|
4
|
-
|
|
5
|
-
constructor(
|
|
1
|
+
import type { IntegrationConnection } from "../resources/IntegrationConnectionsResource";
|
|
2
|
+
import type { AxiosInstance } from "axios";
|
|
3
|
+
export default abstract class BaseIntegration {
|
|
4
|
+
protected readonly httpClient: AxiosInstance;
|
|
5
|
+
constructor(httpClient: AxiosInstance);
|
|
6
6
|
/** Not intended for public use. */
|
|
7
|
-
protected
|
|
7
|
+
protected sendRequest(id: IntegrationConnection["id"], payload: Record<string, unknown>): Promise<object>;
|
|
8
8
|
}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
class BaseIntegration {
|
|
4
|
-
|
|
5
|
-
constructor(
|
|
6
|
-
this.
|
|
4
|
+
httpClient;
|
|
5
|
+
constructor(httpClient) {
|
|
6
|
+
this.httpClient = httpClient;
|
|
7
7
|
}
|
|
8
8
|
/** Not intended for public use. */
|
|
9
|
-
async
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
.then((result) => result.sendIntegrationRequest.response);
|
|
9
|
+
async sendRequest(id, payload) {
|
|
10
|
+
const { data } = await this.httpClient.post(`/integration-connections/${id}/send-request`, payload);
|
|
11
|
+
return data;
|
|
13
12
|
}
|
|
14
13
|
}
|
|
15
14
|
exports.default = BaseIntegration;
|
|
@@ -2404,10 +2404,7 @@ class QbdIntegration extends BaseIntegration_1.default {
|
|
|
2404
2404
|
query: async (integrationConnectionId, params) => this.sendRequestWrapper(integrationConnectionId, { WorkersCompCodeQueryRq: params }, "WorkersCompCodeQueryRs", "WorkersCompCodeRet"),
|
|
2405
2405
|
};
|
|
2406
2406
|
async sendRequestWrapper(integrationConnectionId, params, responseWrapperKey, responseBodyKey) {
|
|
2407
|
-
const response = (await this.
|
|
2408
|
-
integrationConnectionId,
|
|
2409
|
-
requestObject: params,
|
|
2410
|
-
}));
|
|
2407
|
+
const response = (await this.sendRequest(integrationConnectionId, params));
|
|
2411
2408
|
const responseBody = response[responseWrapperKey]?.[responseBodyKey];
|
|
2412
2409
|
if (responseBody === undefined) {
|
|
2413
2410
|
throw new error_1.ConductorIntegrationError({
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addErrorHandlingInterceptors = void 0;
|
|
4
|
+
const error_1 = require("../utils/error");
|
|
5
|
+
const axios_1 = require("axios");
|
|
6
|
+
function addErrorHandlingInterceptors(httpClient) {
|
|
7
|
+
httpClient.interceptors.response.use((response) => response, (error) => {
|
|
8
|
+
// The request was made and the server responded with a status code that
|
|
9
|
+
// falls out of the range of 2xx.
|
|
10
|
+
if (error.response) {
|
|
11
|
+
const errorData = error.response.data;
|
|
12
|
+
if (!(0, error_1.isWellFormedConductorServerError)(errorData)) {
|
|
13
|
+
throw new error_1.ConductorInternalError({
|
|
14
|
+
code: "INVALID_JSON_RESPONSE",
|
|
15
|
+
message: "Invalid JSON received from the Conductor API.",
|
|
16
|
+
httpStatusCode: error.status ?? axios_1.HttpStatusCode.InternalServerError,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
throw (0, error_1.generateConductorErrorFromType)({
|
|
20
|
+
...errorData.error,
|
|
21
|
+
httpStatusCode: error.response.status,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
if (error.code === axios_1.AxiosError.ECONNABORTED) {
|
|
25
|
+
let message = "Request aborted due to timeout being reached";
|
|
26
|
+
if (error.config?.timeout !== undefined) {
|
|
27
|
+
message += ` (${error.config.timeout}ms)`;
|
|
28
|
+
}
|
|
29
|
+
throw new error_1.ConductorConnectionError({
|
|
30
|
+
code: error.code,
|
|
31
|
+
message,
|
|
32
|
+
httpStatusCode: error.status ?? axios_1.HttpStatusCode.RequestTimeout,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// Either the request was made but no response was received (e.g.,
|
|
36
|
+
// Conductor API is offline) or an error ocurred when setting up the
|
|
37
|
+
// request (e.g., no network connection).
|
|
38
|
+
throw new error_1.ConductorConnectionError({
|
|
39
|
+
code: error.code ?? "NETWORK_ERROR",
|
|
40
|
+
message: "An error occurred with our connection to Conductor.",
|
|
41
|
+
httpStatusCode: error.status,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
exports.addErrorHandlingInterceptors = addErrorHandlingInterceptors;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { AxiosInstance, AxiosRequestConfig } from "axios";
|
|
2
|
+
export interface RequestConfigWithStartTime extends AxiosRequestConfig {
|
|
3
|
+
startTime: number;
|
|
4
|
+
}
|
|
5
|
+
export declare function addLoggingInterceptors(httpClient: AxiosInstance, verbose: boolean): void;
|
|
6
|
+
export declare function getDurationStringFromConfig(config: RequestConfigWithStartTime): string;
|
|
7
|
+
export declare function stringifyForLogs(object: unknown): string;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.stringifyForLogs = exports.getDurationStringFromConfig = exports.addLoggingInterceptors = void 0;
|
|
7
|
+
const node_util_1 = __importDefault(require("node:util"));
|
|
8
|
+
function addLoggingInterceptors(httpClient, verbose) {
|
|
9
|
+
httpClient.interceptors.request.use((config) => {
|
|
10
|
+
if (verbose) {
|
|
11
|
+
config.startTime = Date.now();
|
|
12
|
+
console.log("Conductor request:", stringifyForLogs(createRequestLogObject(config)));
|
|
13
|
+
}
|
|
14
|
+
return config;
|
|
15
|
+
});
|
|
16
|
+
httpClient.interceptors.response.use((response) => {
|
|
17
|
+
if (verbose) {
|
|
18
|
+
console.log("Conductor response:", stringifyForLogs(createResponseLogObject(response)));
|
|
19
|
+
}
|
|
20
|
+
return response;
|
|
21
|
+
}, (error) => {
|
|
22
|
+
// Log after the other interceptor wraps the response error in a
|
|
23
|
+
// `ConductorError`.
|
|
24
|
+
// NOTE: We cannot include duration because we lack access to
|
|
25
|
+
// `AxiosError.config` because we already wrapped the error.
|
|
26
|
+
if (verbose) {
|
|
27
|
+
console.log("Conductor error:", stringifyForLogs(error));
|
|
28
|
+
}
|
|
29
|
+
throw error;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
exports.addLoggingInterceptors = addLoggingInterceptors;
|
|
33
|
+
function createRequestLogObject(config) {
|
|
34
|
+
return {
|
|
35
|
+
method: config.method?.toUpperCase(),
|
|
36
|
+
endpoint: config.url,
|
|
37
|
+
body: config.data,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function createResponseLogObject(response) {
|
|
41
|
+
return {
|
|
42
|
+
duration: getDurationStringFromConfig(response.config),
|
|
43
|
+
status: response.status,
|
|
44
|
+
data: response.data,
|
|
45
|
+
request: createRequestLogObject(response.config),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function getDurationStringFromConfig(config) {
|
|
49
|
+
const duration = Date.now() - config.startTime;
|
|
50
|
+
return `${Math.round(duration / 10) / 100}s`;
|
|
51
|
+
}
|
|
52
|
+
exports.getDurationStringFromConfig = getDurationStringFromConfig;
|
|
53
|
+
function stringifyForLogs(object) {
|
|
54
|
+
return node_util_1.default.inspect(object, {
|
|
55
|
+
depth: 5,
|
|
56
|
+
// Omit color codes to keep logs clean when sent to a log management
|
|
57
|
+
// service.
|
|
58
|
+
colors: false,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
exports.stringifyForLogs = stringifyForLogs;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import BaseResource from "../resources/BaseResource";
|
|
2
|
+
export interface IntegrationConnection {
|
|
3
|
+
id: string;
|
|
4
|
+
integrationKey: string;
|
|
5
|
+
endUserSourceId: string;
|
|
6
|
+
endUserEmail: string;
|
|
7
|
+
endUserCompanyName: string;
|
|
8
|
+
}
|
|
9
|
+
export type CreateIntegrationConnectionInput = Pick<IntegrationConnection, "endUserCompanyName" | "endUserEmail" | "endUserSourceId" | "integrationKey">;
|
|
10
|
+
export interface PingIntegrationConnectionOutput {
|
|
11
|
+
duration: number;
|
|
12
|
+
}
|
|
13
|
+
export default class IntegrationConnectionsResource extends BaseResource {
|
|
14
|
+
protected readonly ROUTE = "/integration-connections";
|
|
15
|
+
/**
|
|
16
|
+
* Returns a list of all integration-connections associated with your
|
|
17
|
+
* Conductor account.
|
|
18
|
+
*
|
|
19
|
+
* @returns Your integration-connections.
|
|
20
|
+
*/
|
|
21
|
+
list(): Promise<IntegrationConnection[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new integration-connection.
|
|
24
|
+
*
|
|
25
|
+
* @param input The input object to create the integration-connection.
|
|
26
|
+
* @param input.integrationKey The identifier of the third-party platform to
|
|
27
|
+
* integrate (e.g., "quickbooks-desktop").
|
|
28
|
+
* @param input.endUserSourceId Your end-user's unique ID in your product's
|
|
29
|
+
* database. Must be distinct from your other connections for the same
|
|
30
|
+
* integration.
|
|
31
|
+
* @param input.endUserEmail Your end-user's email address for identification
|
|
32
|
+
* only. No emails will be sent.
|
|
33
|
+
* @param input.endUserCompanyName Your end-user's company name that will be
|
|
34
|
+
* shown elsewhere in Conductor.
|
|
35
|
+
* @returns The newly created integration-connection.
|
|
36
|
+
*/
|
|
37
|
+
create(input: CreateIntegrationConnectionInput): Promise<IntegrationConnection>;
|
|
38
|
+
/**
|
|
39
|
+
* Retrieves the specified integration-connection.
|
|
40
|
+
*
|
|
41
|
+
* @param id The integration-connection ID.
|
|
42
|
+
* @returns The integration-connection.
|
|
43
|
+
*/
|
|
44
|
+
retrieve(id: IntegrationConnection["id"]): Promise<IntegrationConnection>;
|
|
45
|
+
/**
|
|
46
|
+
* Checks whether the specified integration-connection can connect and process
|
|
47
|
+
* requests end-to-end.
|
|
48
|
+
*
|
|
49
|
+
* If the connection fails, the error we encountered will be thrown as a
|
|
50
|
+
* `ConductorError` (like any request). This information is useful for showing
|
|
51
|
+
* a "connection status" indicator in your app. If an error occurs, we
|
|
52
|
+
* recommend displaying the property `error.endUserMessage` to your end-user
|
|
53
|
+
* in your app's UI.
|
|
54
|
+
*
|
|
55
|
+
* @param id The integration-connection ID.
|
|
56
|
+
* @returns The ping result with the duration in milliseconds.
|
|
57
|
+
*/
|
|
58
|
+
ping(id: IntegrationConnection["id"]): Promise<PingIntegrationConnectionOutput>;
|
|
59
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const BaseResource_1 = __importDefault(require("../resources/BaseResource"));
|
|
7
|
+
class IntegrationConnectionsResource extends BaseResource_1.default {
|
|
8
|
+
ROUTE = "/integration-connections";
|
|
9
|
+
/**
|
|
10
|
+
* Returns a list of all integration-connections associated with your
|
|
11
|
+
* Conductor account.
|
|
12
|
+
*
|
|
13
|
+
* @returns Your integration-connections.
|
|
14
|
+
*/
|
|
15
|
+
async list() {
|
|
16
|
+
const { data } = await this.httpClient.get(this.ROUTE);
|
|
17
|
+
return data;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Creates a new integration-connection.
|
|
21
|
+
*
|
|
22
|
+
* @param input The input object to create the integration-connection.
|
|
23
|
+
* @param input.integrationKey The identifier of the third-party platform to
|
|
24
|
+
* integrate (e.g., "quickbooks-desktop").
|
|
25
|
+
* @param input.endUserSourceId Your end-user's unique ID in your product's
|
|
26
|
+
* database. Must be distinct from your other connections for the same
|
|
27
|
+
* integration.
|
|
28
|
+
* @param input.endUserEmail Your end-user's email address for identification
|
|
29
|
+
* only. No emails will be sent.
|
|
30
|
+
* @param input.endUserCompanyName Your end-user's company name that will be
|
|
31
|
+
* shown elsewhere in Conductor.
|
|
32
|
+
* @returns The newly created integration-connection.
|
|
33
|
+
*/
|
|
34
|
+
async create(input) {
|
|
35
|
+
const { data } = await this.httpClient.post(this.ROUTE, input);
|
|
36
|
+
return data;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Retrieves the specified integration-connection.
|
|
40
|
+
*
|
|
41
|
+
* @param id The integration-connection ID.
|
|
42
|
+
* @returns The integration-connection.
|
|
43
|
+
*/
|
|
44
|
+
async retrieve(id) {
|
|
45
|
+
const { data } = await this.httpClient.get(`${this.ROUTE}/${id}`);
|
|
46
|
+
return data;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Checks whether the specified integration-connection can connect and process
|
|
50
|
+
* requests end-to-end.
|
|
51
|
+
*
|
|
52
|
+
* If the connection fails, the error we encountered will be thrown as a
|
|
53
|
+
* `ConductorError` (like any request). This information is useful for showing
|
|
54
|
+
* a "connection status" indicator in your app. If an error occurs, we
|
|
55
|
+
* recommend displaying the property `error.endUserMessage` to your end-user
|
|
56
|
+
* in your app's UI.
|
|
57
|
+
*
|
|
58
|
+
* @param id The integration-connection ID.
|
|
59
|
+
* @returns The ping result with the duration in milliseconds.
|
|
60
|
+
*/
|
|
61
|
+
async ping(id) {
|
|
62
|
+
const { data } = await this.httpClient.get(`${this.ROUTE}/${id}/ping`);
|
|
63
|
+
return data;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.default = IntegrationConnectionsResource;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function checkForUpdates(): void;
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
6
|
+
exports.checkForUpdates = void 0;
|
|
7
7
|
const package_json_1 = __importDefault(require("../../package.json"));
|
|
8
8
|
const node_child_process_1 = require("node:child_process");
|
|
9
9
|
function checkForUpdates() {
|
|
@@ -12,14 +12,7 @@ function checkForUpdates() {
|
|
|
12
12
|
.toString()
|
|
13
13
|
.trim();
|
|
14
14
|
if (currentVersion !== latestVersion) {
|
|
15
|
-
console.warn(`⚠️ A new
|
|
15
|
+
console.warn(`⚠️ A new release of Conductor is available: ${currentVersion} -> ${latestVersion}. To update, run: yarn upgrade ${package_json_1.default.name} --latest`);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
exports.checkForUpdates = checkForUpdates;
|
|
19
|
-
function getServerUrlForEnvironment(environment) {
|
|
20
|
-
if (environment === "production") {
|
|
21
|
-
return "https://api.conductor.is";
|
|
22
|
-
}
|
|
23
|
-
return "http://localhost:4000";
|
|
24
|
-
}
|
|
25
|
-
exports.getServerUrlForEnvironment = getServerUrlForEnvironment;
|
|
@@ -2,23 +2,30 @@ export declare const DEFAULT_END_USER_MESSAGE = "An internal server error occurr
|
|
|
2
2
|
export interface ConductorErrorOptions {
|
|
3
3
|
readonly type: string;
|
|
4
4
|
readonly code: string;
|
|
5
|
+
readonly httpStatusCode?: number | undefined;
|
|
5
6
|
readonly message: string;
|
|
6
7
|
readonly endUserMessage?: string;
|
|
7
|
-
readonly integrationCode?: string;
|
|
8
|
-
readonly httpStatusCode?: number;
|
|
8
|
+
readonly integrationCode?: string | undefined;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* The raw
|
|
11
|
+
* The raw REST error response that Conductor's API returns.
|
|
12
12
|
*/
|
|
13
|
-
export interface
|
|
14
|
-
readonly
|
|
15
|
-
|
|
13
|
+
export interface ConductorServerError {
|
|
14
|
+
readonly error: {
|
|
15
|
+
readonly type: string;
|
|
16
|
+
readonly code: string;
|
|
17
|
+
readonly httpStatusCode: number;
|
|
18
|
+
readonly message: string;
|
|
19
|
+
readonly endUserMessage: string;
|
|
20
|
+
readonly integrationCode?: string;
|
|
21
|
+
};
|
|
16
22
|
}
|
|
23
|
+
export declare function isWellFormedConductorServerError(error: unknown): error is ConductorServerError;
|
|
17
24
|
/**
|
|
18
25
|
* The base error from which all other more specific Conductor errors derive.
|
|
19
26
|
* Specifically for errors that Conductor's API returned.
|
|
20
27
|
*/
|
|
21
|
-
export declare class ConductorError extends Error {
|
|
28
|
+
export declare abstract class ConductorError extends Error {
|
|
22
29
|
/**
|
|
23
30
|
* Categorizes the error.
|
|
24
31
|
*
|
|
@@ -28,12 +35,17 @@ export declare class ConductorError extends Error {
|
|
|
28
35
|
readonly type: string;
|
|
29
36
|
/**
|
|
30
37
|
* The unique error code from Conductor, which is useful for adding special
|
|
31
|
-
* handling for specific errors. E.g., `"
|
|
38
|
+
* handling for specific errors. E.g., `"RESOURCE_MISSING"`,
|
|
32
39
|
* `"API_KEY_INVALID"`, or `"QBD_REQUEST_ERROR"`.
|
|
33
40
|
*
|
|
34
41
|
* In contrast, `type` is more general and categorizes the error.
|
|
35
42
|
*/
|
|
36
43
|
readonly code: string;
|
|
44
|
+
/**
|
|
45
|
+
* The HTTP status code of the response that included the error. You probably
|
|
46
|
+
* won't need this.
|
|
47
|
+
*/
|
|
48
|
+
readonly httpStatusCode: number | undefined;
|
|
37
49
|
/**
|
|
38
50
|
* The developer-friendly error message for your logs.
|
|
39
51
|
*/
|
|
@@ -63,16 +75,11 @@ export declare class ConductorError extends Error {
|
|
|
63
75
|
* should not rely on this code to be the same across integrations.
|
|
64
76
|
*/
|
|
65
77
|
readonly integrationCode: string | undefined;
|
|
66
|
-
/**
|
|
67
|
-
* The HTTP status code of the response that included the error. You probably
|
|
68
|
-
* won't need this.
|
|
69
|
-
*/
|
|
70
|
-
readonly httpStatusCode: number | undefined;
|
|
71
78
|
/**
|
|
72
79
|
* The internal representation of `type` for debugging.
|
|
73
80
|
*/
|
|
74
81
|
protected readonly rawType: string;
|
|
75
|
-
|
|
82
|
+
constructor(options: ConductorErrorOptions);
|
|
76
83
|
}
|
|
77
84
|
type ConductorErrorOptionsWithoutType = Omit<ConductorErrorOptions, "type">;
|
|
78
85
|
/**
|
package/dist/src/utils/error.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/* eslint-disable max-classes-per-file -- Use one module for all error classes. */
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.generateConductorErrorFromType = exports.ConductorUnknownError = exports.ConductorInternalError = exports.ConductorConnectionError = exports.ConductorAuthenticationError = exports.ConductorInvalidRequestError = exports.ConductorIntegrationConnectionError = exports.ConductorIntegrationError = exports.ConductorError = exports.DEFAULT_END_USER_MESSAGE = void 0;
|
|
4
|
+
exports.generateConductorErrorFromType = exports.ConductorUnknownError = exports.ConductorInternalError = exports.ConductorConnectionError = exports.ConductorAuthenticationError = exports.ConductorInvalidRequestError = exports.ConductorIntegrationConnectionError = exports.ConductorIntegrationError = exports.ConductorError = exports.isWellFormedConductorServerError = exports.DEFAULT_END_USER_MESSAGE = void 0;
|
|
5
5
|
// Matches server-side value.
|
|
6
6
|
exports.DEFAULT_END_USER_MESSAGE = "An internal server error occurred. Please try again later.";
|
|
7
|
+
function isWellFormedConductorServerError(error) {
|
|
8
|
+
return (error instanceof Object &&
|
|
9
|
+
typeof error.error === "object" &&
|
|
10
|
+
typeof error.error.type === "string" &&
|
|
11
|
+
typeof error.error.code === "string" &&
|
|
12
|
+
typeof error.error.httpStatusCode === "number" &&
|
|
13
|
+
typeof error.error.message === "string" &&
|
|
14
|
+
typeof error.error.endUserMessage === "string");
|
|
15
|
+
}
|
|
16
|
+
exports.isWellFormedConductorServerError = isWellFormedConductorServerError;
|
|
7
17
|
/**
|
|
8
18
|
* The base error from which all other more specific Conductor errors derive.
|
|
9
19
|
* Specifically for errors that Conductor's API returned.
|
|
@@ -18,12 +28,17 @@ class ConductorError extends Error {
|
|
|
18
28
|
type;
|
|
19
29
|
/**
|
|
20
30
|
* The unique error code from Conductor, which is useful for adding special
|
|
21
|
-
* handling for specific errors. E.g., `"
|
|
31
|
+
* handling for specific errors. E.g., `"RESOURCE_MISSING"`,
|
|
22
32
|
* `"API_KEY_INVALID"`, or `"QBD_REQUEST_ERROR"`.
|
|
23
33
|
*
|
|
24
34
|
* In contrast, `type` is more general and categorizes the error.
|
|
25
35
|
*/
|
|
26
36
|
code;
|
|
37
|
+
/**
|
|
38
|
+
* The HTTP status code of the response that included the error. You probably
|
|
39
|
+
* won't need this.
|
|
40
|
+
*/
|
|
41
|
+
httpStatusCode;
|
|
27
42
|
/**
|
|
28
43
|
* The developer-friendly error message for your logs.
|
|
29
44
|
*/
|
|
@@ -53,11 +68,6 @@ class ConductorError extends Error {
|
|
|
53
68
|
* should not rely on this code to be the same across integrations.
|
|
54
69
|
*/
|
|
55
70
|
integrationCode;
|
|
56
|
-
/**
|
|
57
|
-
* The HTTP status code of the response that included the error. You probably
|
|
58
|
-
* won't need this.
|
|
59
|
-
*/
|
|
60
|
-
httpStatusCode;
|
|
61
71
|
/**
|
|
62
72
|
* The internal representation of `type` for debugging.
|
|
63
73
|
*/
|
|
@@ -78,10 +88,10 @@ class ConductorError extends Error {
|
|
|
78
88
|
this.type = this.constructor.name;
|
|
79
89
|
this.rawType = options.type;
|
|
80
90
|
this.code = options.code;
|
|
91
|
+
this.httpStatusCode = options.httpStatusCode;
|
|
81
92
|
this.message = options.message;
|
|
82
93
|
this.endUserMessage = options.endUserMessage ?? exports.DEFAULT_END_USER_MESSAGE;
|
|
83
94
|
this.integrationCode = options.integrationCode;
|
|
84
|
-
this.httpStatusCode = options.httpStatusCode;
|
|
85
95
|
}
|
|
86
96
|
}
|
|
87
97
|
exports.ConductorError = ConductorError;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getServerUrlForEnvironment = void 0;
|
|
4
|
+
function getServerUrlForEnvironment(environment) {
|
|
5
|
+
if (environment === "production") {
|
|
6
|
+
return "https://api.conductor.is";
|
|
7
|
+
}
|
|
8
|
+
return "http://localhost:4000";
|
|
9
|
+
}
|
|
10
|
+
exports.getServerUrlForEnvironment = getServerUrlForEnvironment;
|
package/package.json
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conductor-node",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "Easily integrate
|
|
5
|
-
"author": "Danny Nemer <hi@DannyNemer.com>",
|
|
3
|
+
"version": "10.0.0",
|
|
4
|
+
"description": "Easily integrate the entire QuickBooks Desktop API using fully-typed async TypeScript",
|
|
6
5
|
"license": "MIT",
|
|
7
6
|
"type": "commonjs",
|
|
8
7
|
"main": "dist/src/index.js",
|
|
@@ -13,10 +12,9 @@
|
|
|
13
12
|
],
|
|
14
13
|
"scripts": {
|
|
15
14
|
"prepack": "yarn --silent tsc && yarn --silent delete-compiled-dev-files && yarn --silent tsc-alias",
|
|
16
|
-
"delete-compiled-dev-files": "rm -rf `find ./dist -type d -name __tests__` ./dist/src/utils/
|
|
15
|
+
"delete-compiled-dev-files": "rm -rf `find ./dist -type d -name __tests__` ./dist/src/utils/test/*",
|
|
17
16
|
"postpack": "rm -rf dist",
|
|
18
17
|
"clean": "rm -rf dist package conductor-node-*.tgz tsconfig.tsbuildinfo",
|
|
19
|
-
"gen:graphql": "yarn graphql-codegen --config=./src/graphql/codegenConfig.ts",
|
|
20
18
|
"diff": "yarn pack && npm diff --diff=conductor-node@latest --diff=conductor-node-v$(node -p \"require('./package.json').version\").tgz && yarn clean",
|
|
21
19
|
"prepublishOnly": "yarn test",
|
|
22
20
|
"test": "yarn --cwd=../../ test ./packages/client",
|
|
@@ -26,25 +24,17 @@
|
|
|
26
24
|
"node": ">=16"
|
|
27
25
|
},
|
|
28
26
|
"dependencies": {
|
|
29
|
-
"
|
|
30
|
-
"graphql-request": "~5.1.0"
|
|
27
|
+
"axios": "^1.4.0"
|
|
31
28
|
},
|
|
32
29
|
"devDependencies": {
|
|
33
|
-
"
|
|
34
|
-
"@graphql-codegen/cli": "^3.2.1",
|
|
35
|
-
"@graphql-codegen/typescript-graphql-request": "^4.5.8",
|
|
36
|
-
"@graphql-codegen/typescript-operations": "^3.0.1",
|
|
30
|
+
"axios-mock-adapter": "^1.21.4",
|
|
37
31
|
"tsc-alias": "^1.7.0"
|
|
38
32
|
},
|
|
39
33
|
"keywords": [
|
|
40
|
-
"accounting",
|
|
41
|
-
"api",
|
|
42
34
|
"conductor",
|
|
43
35
|
"qbd",
|
|
44
36
|
"qbwc",
|
|
45
37
|
"qbxml",
|
|
46
|
-
"quickbooks desktop"
|
|
47
|
-
"sdk",
|
|
48
|
-
"typescript"
|
|
38
|
+
"quickbooks desktop"
|
|
49
39
|
]
|
|
50
40
|
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import type { GraphQLClient } from "graphql-request";
|
|
2
|
-
import type * as Dom from "graphql-request/dist/types.dom";
|
|
3
|
-
export type Maybe<T> = T | null;
|
|
4
|
-
export type InputMaybe<T> = Maybe<T>;
|
|
5
|
-
export type Exact<T extends {
|
|
6
|
-
[key: string]: unknown;
|
|
7
|
-
}> = {
|
|
8
|
-
[K in keyof T]: T[K];
|
|
9
|
-
};
|
|
10
|
-
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & {
|
|
11
|
-
[SubKey in K]?: Maybe<T[SubKey]>;
|
|
12
|
-
};
|
|
13
|
-
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & {
|
|
14
|
-
[SubKey in K]: Maybe<T[SubKey]>;
|
|
15
|
-
};
|
|
16
|
-
/** All built-in and custom scalars, mapped to their actual values */
|
|
17
|
-
export type Scalars = {
|
|
18
|
-
ID: string;
|
|
19
|
-
String: string;
|
|
20
|
-
Boolean: boolean;
|
|
21
|
-
Int: number;
|
|
22
|
-
Float: number;
|
|
23
|
-
DateTime: Date;
|
|
24
|
-
JSONObject: object;
|
|
25
|
-
};
|
|
26
|
-
export type GraphqlCreateIntegrationConnectionInput = {
|
|
27
|
-
endUserCompanyName: Scalars["String"];
|
|
28
|
-
endUserEmail: Scalars["String"];
|
|
29
|
-
endUserSourceId: Scalars["String"];
|
|
30
|
-
integrationKey: Scalars["String"];
|
|
31
|
-
};
|
|
32
|
-
export type GraphqlPingIntegrationConnectionInput = {
|
|
33
|
-
integrationConnectionId: Scalars["ID"];
|
|
34
|
-
};
|
|
35
|
-
export type GraphqlSendIntegrationRequestInput = {
|
|
36
|
-
integrationConnectionId: Scalars["ID"];
|
|
37
|
-
requestObject: Scalars["JSONObject"];
|
|
38
|
-
};
|
|
39
|
-
export type GraphqlIntegrationConnectionFragment = {
|
|
40
|
-
id: string;
|
|
41
|
-
integrationKey: string;
|
|
42
|
-
endUserSourceId: string;
|
|
43
|
-
endUserEmail: string;
|
|
44
|
-
endUserCompanyName: string;
|
|
45
|
-
lastHeartbeatAt: Date | null;
|
|
46
|
-
};
|
|
47
|
-
export type GraphqlGetIntegrationConnectionQueryVariables = Exact<{
|
|
48
|
-
integrationConnectionId: Scalars["ID"];
|
|
49
|
-
}>;
|
|
50
|
-
export type GraphqlGetIntegrationConnectionQuery = {
|
|
51
|
-
integrationConnection: {
|
|
52
|
-
id: string;
|
|
53
|
-
integrationKey: string;
|
|
54
|
-
endUserSourceId: string;
|
|
55
|
-
endUserEmail: string;
|
|
56
|
-
endUserCompanyName: string;
|
|
57
|
-
lastHeartbeatAt: Date | null;
|
|
58
|
-
};
|
|
59
|
-
};
|
|
60
|
-
export type GraphqlGetIntegrationConnectionsQueryVariables = Exact<{
|
|
61
|
-
[key: string]: never;
|
|
62
|
-
}>;
|
|
63
|
-
export type GraphqlGetIntegrationConnectionsQuery = {
|
|
64
|
-
integrationConnections: Array<{
|
|
65
|
-
id: string;
|
|
66
|
-
integrationKey: string;
|
|
67
|
-
endUserSourceId: string;
|
|
68
|
-
endUserEmail: string;
|
|
69
|
-
endUserCompanyName: string;
|
|
70
|
-
lastHeartbeatAt: Date | null;
|
|
71
|
-
}>;
|
|
72
|
-
};
|
|
73
|
-
export type GraphqlCreateIntegrationConnectionMutationVariables = Exact<{
|
|
74
|
-
input: GraphqlCreateIntegrationConnectionInput;
|
|
75
|
-
}>;
|
|
76
|
-
export type GraphqlCreateIntegrationConnectionMutation = {
|
|
77
|
-
createIntegrationConnection: {
|
|
78
|
-
integrationConnection: {
|
|
79
|
-
id: string;
|
|
80
|
-
integrationKey: string;
|
|
81
|
-
endUserSourceId: string;
|
|
82
|
-
endUserEmail: string;
|
|
83
|
-
endUserCompanyName: string;
|
|
84
|
-
lastHeartbeatAt: Date | null;
|
|
85
|
-
};
|
|
86
|
-
};
|
|
87
|
-
};
|
|
88
|
-
export type GraphqlPingIntegrationConnectionMutationVariables = Exact<{
|
|
89
|
-
input: GraphqlPingIntegrationConnectionInput;
|
|
90
|
-
}>;
|
|
91
|
-
export type GraphqlPingIntegrationConnectionMutation = {
|
|
92
|
-
pingIntegrationConnection: {
|
|
93
|
-
duration: number;
|
|
94
|
-
};
|
|
95
|
-
};
|
|
96
|
-
export type GraphqlSendIntegrationRequestMutationVariables = Exact<{
|
|
97
|
-
input: GraphqlSendIntegrationRequestInput;
|
|
98
|
-
}>;
|
|
99
|
-
export type GraphqlSendIntegrationRequestMutation = {
|
|
100
|
-
sendIntegrationRequest: {
|
|
101
|
-
response: object;
|
|
102
|
-
};
|
|
103
|
-
};
|
|
104
|
-
export declare const IntegrationConnectionFragmentDoc = "\n fragment IntegrationConnection on IntegrationConnection {\n id\n integrationKey\n endUserSourceId\n endUserEmail\n endUserCompanyName\n lastHeartbeatAt\n}\n ";
|
|
105
|
-
export declare const GetIntegrationConnectionDocument: string;
|
|
106
|
-
export declare const GetIntegrationConnectionsDocument: string;
|
|
107
|
-
export declare const CreateIntegrationConnectionDocument: string;
|
|
108
|
-
export declare const PingIntegrationConnectionDocument = "\n mutation pingIntegrationConnection($input: PingIntegrationConnectionInput!) {\n pingIntegrationConnection(input: $input) {\n duration\n }\n}\n ";
|
|
109
|
-
export declare const SendIntegrationRequestDocument = "\n mutation sendIntegrationRequest($input: SendIntegrationRequestInput!) {\n sendIntegrationRequest(input: $input) {\n response\n }\n}\n ";
|
|
110
|
-
export type SdkFunctionWrapper = <T>(action: (requestHeaders?: Record<string, string>) => Promise<T>, operationName: string, operationType?: string) => Promise<T>;
|
|
111
|
-
export declare function getSdk(client: GraphQLClient, withWrapper?: SdkFunctionWrapper): {
|
|
112
|
-
getIntegrationConnection(variables: GraphqlGetIntegrationConnectionQueryVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<GraphqlGetIntegrationConnectionQuery>;
|
|
113
|
-
getIntegrationConnections(variables?: GraphqlGetIntegrationConnectionsQueryVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<GraphqlGetIntegrationConnectionsQuery>;
|
|
114
|
-
createIntegrationConnection(variables: GraphqlCreateIntegrationConnectionMutationVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<GraphqlCreateIntegrationConnectionMutation>;
|
|
115
|
-
pingIntegrationConnection(variables: GraphqlPingIntegrationConnectionMutationVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<GraphqlPingIntegrationConnectionMutation>;
|
|
116
|
-
sendIntegrationRequest(variables: GraphqlSendIntegrationRequestMutationVariables, requestHeaders?: Dom.RequestInit["headers"]): Promise<GraphqlSendIntegrationRequestMutation>;
|
|
117
|
-
};
|
|
118
|
-
export type Sdk = ReturnType<typeof getSdk>;
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getSdk = exports.SendIntegrationRequestDocument = exports.PingIntegrationConnectionDocument = exports.CreateIntegrationConnectionDocument = exports.GetIntegrationConnectionsDocument = exports.GetIntegrationConnectionDocument = exports.IntegrationConnectionFragmentDoc = void 0;
|
|
4
|
-
exports.IntegrationConnectionFragmentDoc = `
|
|
5
|
-
fragment IntegrationConnection on IntegrationConnection {
|
|
6
|
-
id
|
|
7
|
-
integrationKey
|
|
8
|
-
endUserSourceId
|
|
9
|
-
endUserEmail
|
|
10
|
-
endUserCompanyName
|
|
11
|
-
lastHeartbeatAt
|
|
12
|
-
}
|
|
13
|
-
`;
|
|
14
|
-
exports.GetIntegrationConnectionDocument = `
|
|
15
|
-
query getIntegrationConnection($integrationConnectionId: ID!) {
|
|
16
|
-
integrationConnection(id: $integrationConnectionId) {
|
|
17
|
-
...IntegrationConnection
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
${exports.IntegrationConnectionFragmentDoc}`;
|
|
21
|
-
exports.GetIntegrationConnectionsDocument = `
|
|
22
|
-
query getIntegrationConnections {
|
|
23
|
-
integrationConnections {
|
|
24
|
-
...IntegrationConnection
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
${exports.IntegrationConnectionFragmentDoc}`;
|
|
28
|
-
exports.CreateIntegrationConnectionDocument = `
|
|
29
|
-
mutation createIntegrationConnection($input: CreateIntegrationConnectionInput!) {
|
|
30
|
-
createIntegrationConnection(input: $input) {
|
|
31
|
-
integrationConnection {
|
|
32
|
-
...IntegrationConnection
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
${exports.IntegrationConnectionFragmentDoc}`;
|
|
37
|
-
exports.PingIntegrationConnectionDocument = `
|
|
38
|
-
mutation pingIntegrationConnection($input: PingIntegrationConnectionInput!) {
|
|
39
|
-
pingIntegrationConnection(input: $input) {
|
|
40
|
-
duration
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
`;
|
|
44
|
-
exports.SendIntegrationRequestDocument = `
|
|
45
|
-
mutation sendIntegrationRequest($input: SendIntegrationRequestInput!) {
|
|
46
|
-
sendIntegrationRequest(input: $input) {
|
|
47
|
-
response
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
`;
|
|
51
|
-
const defaultWrapper = (action, _operationName, _operationType) => action();
|
|
52
|
-
function getSdk(client, withWrapper = defaultWrapper) {
|
|
53
|
-
return {
|
|
54
|
-
getIntegrationConnection(variables, requestHeaders) {
|
|
55
|
-
return withWrapper((wrappedRequestHeaders) => client.request(exports.GetIntegrationConnectionDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "getIntegrationConnection", "query");
|
|
56
|
-
},
|
|
57
|
-
getIntegrationConnections(variables, requestHeaders) {
|
|
58
|
-
return withWrapper((wrappedRequestHeaders) => client.request(exports.GetIntegrationConnectionsDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "getIntegrationConnections", "query");
|
|
59
|
-
},
|
|
60
|
-
createIntegrationConnection(variables, requestHeaders) {
|
|
61
|
-
return withWrapper((wrappedRequestHeaders) => client.request(exports.CreateIntegrationConnectionDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "createIntegrationConnection", "mutation");
|
|
62
|
-
},
|
|
63
|
-
pingIntegrationConnection(variables, requestHeaders) {
|
|
64
|
-
return withWrapper((wrappedRequestHeaders) => client.request(exports.PingIntegrationConnectionDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "pingIntegrationConnection", "mutation");
|
|
65
|
-
},
|
|
66
|
-
sendIntegrationRequest(variables, requestHeaders) {
|
|
67
|
-
return withWrapper((wrappedRequestHeaders) => client.request(exports.SendIntegrationRequestDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "sendIntegrationRequest", "mutation");
|
|
68
|
-
},
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
exports.getSdk = getSdk;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { getSdk } from "../graphql/__generated__/operationTypes";
|
|
2
|
-
import type { ConductorError } from "../utils/error";
|
|
3
|
-
export declare function wrapGraphqlOperations<T extends ReturnType<typeof getSdk>>(graphqlOperations: T, verbose: boolean): T;
|
|
4
|
-
export declare function graphqlOperationWrapper<V extends {
|
|
5
|
-
[key: string]: unknown;
|
|
6
|
-
}, R>(operationName: string, variables: V | undefined, operation: (variables: V | undefined) => Promise<R>, verbose: boolean): Promise<R>;
|
|
7
|
-
export declare function getDurationString(startTime: number): string;
|
|
8
|
-
export declare function wrapError(error: Error): ConductorError;
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.wrapError = exports.getDurationString = exports.graphqlOperationWrapper = exports.wrapGraphqlOperations = void 0;
|
|
7
|
-
const error_1 = require("../utils/error");
|
|
8
|
-
const graphql_request_1 = require("graphql-request");
|
|
9
|
-
const node_util_1 = __importDefault(require("node:util"));
|
|
10
|
-
function wrapGraphqlOperations(graphqlOperations, verbose) {
|
|
11
|
-
return Object.fromEntries(Object.entries(graphqlOperations).map(([operationName, operation]) => [
|
|
12
|
-
operationName,
|
|
13
|
-
async (variables) => graphqlOperationWrapper(operationName,
|
|
14
|
-
// @ts-expect-error -- It is safe to call `operation` with `variables`.
|
|
15
|
-
variables, operation, verbose),
|
|
16
|
-
]));
|
|
17
|
-
}
|
|
18
|
-
exports.wrapGraphqlOperations = wrapGraphqlOperations;
|
|
19
|
-
async function graphqlOperationWrapper(operationName, variables, operation, verbose) {
|
|
20
|
-
const graphqlInfo = {
|
|
21
|
-
operationName,
|
|
22
|
-
input: variables
|
|
23
|
-
? Object.keys(variables).length === 1 && "input" in variables
|
|
24
|
-
? variables["input"] // Flatten the input if it only has one key called "input".
|
|
25
|
-
: variables
|
|
26
|
-
: {},
|
|
27
|
-
};
|
|
28
|
-
if (verbose) {
|
|
29
|
-
console.log(`Conductor request start: ${node_util_1.default.inspect(graphqlInfo, {
|
|
30
|
-
depth: undefined,
|
|
31
|
-
})}`);
|
|
32
|
-
}
|
|
33
|
-
const startTime = Date.now();
|
|
34
|
-
try {
|
|
35
|
-
const result = await operation(variables);
|
|
36
|
-
if (verbose) {
|
|
37
|
-
const responseLog = {
|
|
38
|
-
duration: getDurationString(startTime),
|
|
39
|
-
...graphqlInfo,
|
|
40
|
-
};
|
|
41
|
-
const responseString = node_util_1.default.inspect(responseLog, { depth: undefined });
|
|
42
|
-
console.log(`Conductor request complete: ${responseString}`);
|
|
43
|
-
}
|
|
44
|
-
return result;
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
const conductorError = wrapError(error);
|
|
48
|
-
if (verbose) {
|
|
49
|
-
const errorLog = {
|
|
50
|
-
duration: getDurationString(startTime),
|
|
51
|
-
error: String(conductorError),
|
|
52
|
-
...graphqlInfo,
|
|
53
|
-
};
|
|
54
|
-
const errorString = node_util_1.default.inspect(errorLog, { depth: undefined });
|
|
55
|
-
console.log(`Conductor error: ${errorString}`);
|
|
56
|
-
}
|
|
57
|
-
throw conductorError;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
exports.graphqlOperationWrapper = graphqlOperationWrapper;
|
|
61
|
-
function getDurationString(startTime) {
|
|
62
|
-
const duration = Date.now() - startTime;
|
|
63
|
-
return `${Math.round(duration / 10) / 100}s`;
|
|
64
|
-
}
|
|
65
|
-
exports.getDurationString = getDurationString;
|
|
66
|
-
function wrapError(error) {
|
|
67
|
-
if (error instanceof graphql_request_1.ClientError) {
|
|
68
|
-
const { response } = error;
|
|
69
|
-
if ([404, 502, 503].includes(response.status)) {
|
|
70
|
-
return createConnectionError(String(response.status), response.status);
|
|
71
|
-
}
|
|
72
|
-
const nestedError = response.errors?.[0];
|
|
73
|
-
const errorExtensions = nestedError?.extensions;
|
|
74
|
-
if (errorExtensions) {
|
|
75
|
-
// Do *not* bother validating if this error is a Conductor error:
|
|
76
|
-
// 1. We know this is a `ClientError`, so `message` and `httpStatusCode`
|
|
77
|
-
// exist.
|
|
78
|
-
// 2. We know this is a GraphQL error, so `code` likely exists either from
|
|
79
|
-
// Conductor or Apollo.
|
|
80
|
-
// 3. If `type` is absent, then `generateConductorErrorFromType` will
|
|
81
|
-
// default to `ConductorUnknownError`.
|
|
82
|
-
// 4. If `endUserMessage` is absent, then `ConductorError` will use a
|
|
83
|
-
// default value.
|
|
84
|
-
return (0, error_1.generateConductorErrorFromType)({
|
|
85
|
-
message: nestedError.message,
|
|
86
|
-
httpStatusCode: response.status,
|
|
87
|
-
...errorExtensions,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
// Ideally, we would check for instances of `FetchError` but we don't have
|
|
91
|
-
// that type available.
|
|
92
|
-
}
|
|
93
|
-
else if (error.name === "FetchError") {
|
|
94
|
-
return createConnectionError(error.message, 502);
|
|
95
|
-
}
|
|
96
|
-
return new error_1.ConductorInternalError({
|
|
97
|
-
code: "INVALID_JSON_RESPONSE",
|
|
98
|
-
message: "Invalid JSON received from the Conductor API.",
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
exports.wrapError = wrapError;
|
|
102
|
-
function createConnectionError(originalMessage, httpStatusCode) {
|
|
103
|
-
return new error_1.ConductorConnectionError({
|
|
104
|
-
code: "SERVER_UNAVAILABLE",
|
|
105
|
-
message: `Conductor failed to connect to the server: ${originalMessage}. Please alert the Conductor team if this error persists.`,
|
|
106
|
-
httpStatusCode,
|
|
107
|
-
});
|
|
108
|
-
}
|