conductor-node 8.2.4 → 8.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,8 +2,18 @@
2
2
 
3
3
  Execute _any_ read or write [QuickBooks Desktop API](https://developer.intuit.com/app/developer/qbdesktop/docs/api-reference/qbdesktop) through async TypeScript and receive a fully-typed response.
4
4
 
5
+ <!-- markdownlint-disable MD033 -->
5
6
  <img src="https://user-images.githubusercontent.com/170023/213273732-83dd6881-0b36-4787-820b-bd55cdc8444f.jpg" alt="qbd" width="600"/>
6
7
 
8
+ ## Table of Contents
9
+
10
+ 1. [Requirements](#requirements)
11
+ 2. [Installation](#installation)
12
+ 3. [Usage](#usage)
13
+ 4. [APIs](#apis)
14
+ 5. [TypeScript](#typescript)
15
+ 6. [Error Handling](#error-handling)
16
+
7
17
  ## Requirements
8
18
 
9
19
  1. A Conductor API key from Danny.
@@ -34,7 +44,7 @@ const newAccount = await conductor.qbd.account.add(qbdConnections[0].id, {
34
44
  });
35
45
  ```
36
46
 
37
- ## `Conductor` APIs
47
+ ## APIs
38
48
 
39
49
  ### `createIntegrationConnection(input: CreateIntegrationConnectionInput)`
40
50
 
@@ -100,34 +110,39 @@ const qbdConnection = await conductor.getIntegrationConnectionById(
100
110
  );
101
111
  ```
102
112
 
103
- ### `getConnectionStatus(id: string)`
113
+ ### `pingIntegrationConnection(id: string)`
104
114
 
105
- Check whether we can successfully connect to the end-user's QBD instance.
115
+ Check whether the specified integration-connection can connect and process requests end-to-end.
106
116
 
107
- Unlike `lastHeartbeatAt`, which only checks if QBWC is running (i.e., is the user's computer on), this check fails if the user's computer is on but QBD is not running, the wrong company file is open, or there is a modal dialog open in QBD.
117
+ 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.
118
+
119
+ In the form of a rejected promise:
108
120
 
109
121
  ```ts
110
- const connectionStatus = await conductor.getConnectionStatus(qbdConnectionId);
122
+ conductor.pingIntegrationConnection(qbdConnectionId).catch((error) => {
123
+ if (error instanceof ConductorError) {
124
+ // Update your app's UI to display `error.endUserMessage`.
125
+ }
126
+ // ...
127
+ });
111
128
  ```
112
129
 
113
- The response includes the following:
130
+ Or using `async`/`await`:
114
131
 
115
132
  ```ts
116
- {
117
- // Whether we can connect to the end-user's QuickBooks Desktop.
118
- isConnected: false,
119
- // If `isConnected=false`, an `error` like the following will exist:
120
- error: {
121
- code: "QBWC_CONNECTION_ERROR",
122
- developerMessage: "QBWC Error 0x80040408: Could not start QuickBooks.",
123
- endUserMessage: "An error occurred while connecting to QuickBooks Desktop. Please ensure QuickBooks Desktop is running and the correct company file is open, then try again.",
133
+ try {
134
+ await conductor.pingIntegrationConnection(qbdConnectionId);
135
+ } catch (error) {
136
+ if (error instanceof ConductorError) {
137
+ // Update your app's UI to display `error.endUserMessage`.
124
138
  }
139
+ // ...
125
140
  }
126
141
  ```
127
142
 
128
143
  ## TypeScript
129
144
 
130
- Access the full QuickBooks Desktop API through TypeScript. The `qbd.*` APIs are fully-typed with inline documentation and will autocomplete in your editor.
145
+ Access the full QuickBooks Desktop API through TypeScript. The `qbd.*` APIs are fully typed with inline documentation and will autocomplete in your editor.
131
146
 
132
147
  To manually access the QBD types, import them from `conductor-node` like so:
133
148
 
@@ -140,3 +155,73 @@ const accountAddInput: QbdTypes.AccountAdd = {
140
155
  OpenBalance: "100",
141
156
  };
142
157
  ```
158
+
159
+ ## Error Handling
160
+
161
+ The `ConductorError` has the following properties:
162
+
163
+ ```ts
164
+ {
165
+ // The error type.
166
+ type: string;
167
+ // The error code.
168
+ code: string;
169
+ // The developer-friendly error message for your logs.
170
+ developerMessage: string;
171
+ // The end-user-friendly error message to display in your app.
172
+ endUserMessage: string;
173
+ // The HTTP status code.
174
+ statusCode: number | undefined;
175
+ }
176
+ ```
177
+
178
+ ### Error Types
179
+
180
+ The error object you receive will have one of the following category types:
181
+
182
+ | Type | Description |
183
+ | ----------------------- | ----------------------------------------------------------------------------------------- |
184
+ | `authentication_error` | Conductor can't authenticate you with the information provided. E.g. incorrect API key. |
185
+ | `connection_error` | There was a network problem between your server and Conductor. |
186
+ | `integration_error` | An error occurred on the third-party integration's end while processing your request. |
187
+ | `internal_error` | Something went wrong on Conductor's end. (These are rare.) |
188
+ | `invalid_request_error` | You made an API call with the wrong parameters, in the wrong state, or in an invalid way. |
189
+
190
+ ### Example
191
+
192
+ Using `async`/`await`:
193
+
194
+ ```ts
195
+ try {
196
+ const newAccount = await conductor.qbd.account.add(qbdConnectionId, {
197
+ Name: "Test Account",
198
+ AccountType: "Bank",
199
+ OpenBalance: "100",
200
+ });
201
+ } catch (error) {
202
+ if (error instanceof ConductorError) {
203
+ // Update your app's UI to display `error.endUserMessage`.
204
+ }
205
+ // ...
206
+ }
207
+ ```
208
+
209
+ Or in the form of a rejected promise:
210
+
211
+ ```ts
212
+ conductor.qbd.account
213
+ .add(qbdConnectionId, {
214
+ Name: "Test Account",
215
+ AccountType: "Bank",
216
+ OpenBalance: "100",
217
+ })
218
+ .then((newAccount) => {
219
+ // ...
220
+ })
221
+ .catch((error) => {
222
+ if (error instanceof ConductorError) {
223
+ // Update your app's UI to display `error.endUserMessage`.
224
+ }
225
+ // ...
226
+ });
227
+ ```
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conductor-node",
3
- "version": "8.2.4",
3
+ "version": "8.3.0",
4
4
  "description": "Easily integrate with the entire QuickBooks Desktop API with fully-typed async TypeScript",
5
5
  "author": "Danny Nemer <hi@DannyNemer.com>",
6
6
  "license": "MIT",
@@ -1,4 +1,4 @@
1
- import type { GraphqlCreateIntegrationConnectionInput, GraphqlCreateIntegrationConnectionMutation, GraphqlGetConnectionStatusQuery, GraphqlGetConnectionStatusQueryVariables, GraphqlGetIntegrationConnectionQuery, GraphqlGetIntegrationConnectionQueryVariables, GraphqlGetIntegrationConnectionsQuery } from "./graphql/__generated__/operationTypes";
1
+ import type { GraphqlCreateIntegrationConnectionInput, GraphqlCreateIntegrationConnectionMutation, GraphqlGetConnectionStatusQuery, GraphqlGetConnectionStatusQueryVariables, GraphqlGetIntegrationConnectionQuery, GraphqlGetIntegrationConnectionQueryVariables, GraphqlGetIntegrationConnectionsQuery, GraphqlPingIntegrationConnectionMutation, GraphqlPingIntegrationConnectionMutationVariables } from "./graphql/__generated__/operationTypes";
2
2
  import QbdIntegration from "./integrations/qbd/QbdIntegration";
3
3
  import { getServerUrlForEnvironment } from "./utils";
4
4
  export interface ClientOptions {
@@ -62,5 +62,14 @@ export default class Client {
62
62
  createIntegrationConnection(input: GraphqlCreateIntegrationConnectionInput & {
63
63
  integrationKey: "quickbooks-desktop";
64
64
  }): Promise<GraphqlCreateIntegrationConnectionMutation["createIntegrationConnection"]["integrationConnection"]>;
65
+ /**
66
+ * Check whether the specified integration-connection can connect and process
67
+ * requests end-to-end.
68
+ *
69
+ * If the connection fails, the error we encountered will be thrown as a
70
+ * `ConductorError`. This information is useful for showing a "connection
71
+ * status" indicator in your app.
72
+ */
73
+ pingIntegrationConnection(integrationConnectionId: GraphqlPingIntegrationConnectionMutationVariables["input"]["integrationConnectionId"]): Promise<GraphqlPingIntegrationConnectionMutation["pingIntegrationConnection"]>;
65
74
  private createHeaders;
66
75
  }
@@ -84,6 +84,19 @@ class Client {
84
84
  .createIntegrationConnection({ input })
85
85
  .then((result) => result.createIntegrationConnection.integrationConnection);
86
86
  }
87
+ /**
88
+ * Check whether the specified integration-connection can connect and process
89
+ * requests end-to-end.
90
+ *
91
+ * If the connection fails, the error we encountered will be thrown as a
92
+ * `ConductorError`. This information is useful for showing a "connection
93
+ * status" indicator in your app.
94
+ */
95
+ async pingIntegrationConnection(integrationConnectionId) {
96
+ return this.graphqlOperations
97
+ .pingIntegrationConnection({ input: { integrationConnectionId } })
98
+ .then((result) => result.pingIntegrationConnection);
99
+ }
87
100
  createHeaders(apiKey) {
88
101
  return {
89
102
  Authorization: `Bearer ${apiKey}`,
@@ -29,6 +29,9 @@ export type GraphqlCreateIntegrationConnectionInput = {
29
29
  endUserSourceId: Scalars["String"];
30
30
  integrationKey: Scalars["String"];
31
31
  };
32
+ export type GraphqlPingIntegrationConnectionInput = {
33
+ integrationConnectionId: Scalars["ID"];
34
+ };
32
35
  export type GraphqlSendIntegrationRequestInput = {
33
36
  integrationConnectionId: Scalars["ID"];
34
37
  requestObject: Scalars["JSONObject"];
@@ -105,6 +108,14 @@ export type GraphqlCreateIntegrationConnectionMutation = {
105
108
  };
106
109
  };
107
110
  };
111
+ export type GraphqlPingIntegrationConnectionMutationVariables = Exact<{
112
+ input: GraphqlPingIntegrationConnectionInput;
113
+ }>;
114
+ export type GraphqlPingIntegrationConnectionMutation = {
115
+ pingIntegrationConnection: {
116
+ duration: number;
117
+ };
118
+ };
108
119
  export type GraphqlSendIntegrationRequestMutationVariables = Exact<{
109
120
  input: GraphqlSendIntegrationRequestInput;
110
121
  }>;
@@ -119,6 +130,7 @@ export declare const GetIntegrationConnectionDocument: string;
119
130
  export declare const GetIntegrationConnectionsDocument: string;
120
131
  export declare const GetConnectionStatusDocument: string;
121
132
  export declare const CreateIntegrationConnectionDocument: string;
133
+ export declare const PingIntegrationConnectionDocument = "\n mutation pingIntegrationConnection($input: PingIntegrationConnectionInput!) {\n pingIntegrationConnection(input: $input) {\n duration\n }\n}\n ";
122
134
  export declare const SendIntegrationRequestDocument = "\n mutation sendIntegrationRequest($input: SendIntegrationRequestInput!) {\n sendIntegrationRequest(input: $input) {\n response\n }\n}\n ";
123
135
  export type SdkFunctionWrapper = <T>(action: (requestHeaders?: Record<string, string>) => Promise<T>, operationName: string, operationType?: string) => Promise<T>;
124
136
  export declare function getSdk(client: GraphQLClient, withWrapper?: SdkFunctionWrapper): {
@@ -126,6 +138,7 @@ export declare function getSdk(client: GraphQLClient, withWrapper?: SdkFunctionW
126
138
  getIntegrationConnections(variables?: GraphqlGetIntegrationConnectionsQueryVariables, requestHeaders?: (Record<string, string> | Dom.Headers | string[][]) | undefined): Promise<GraphqlGetIntegrationConnectionsQuery>;
127
139
  getConnectionStatus(variables: GraphqlGetConnectionStatusQueryVariables, requestHeaders?: (Record<string, string> | Dom.Headers | string[][]) | undefined): Promise<GraphqlGetConnectionStatusQuery>;
128
140
  createIntegrationConnection(variables: GraphqlCreateIntegrationConnectionMutationVariables, requestHeaders?: (Record<string, string> | Dom.Headers | string[][]) | undefined): Promise<GraphqlCreateIntegrationConnectionMutation>;
141
+ pingIntegrationConnection(variables: GraphqlPingIntegrationConnectionMutationVariables, requestHeaders?: (Record<string, string> | Dom.Headers | string[][]) | undefined): Promise<GraphqlPingIntegrationConnectionMutation>;
129
142
  sendIntegrationRequest(variables: GraphqlSendIntegrationRequestMutationVariables, requestHeaders?: (Record<string, string> | Dom.Headers | string[][]) | undefined): Promise<GraphqlSendIntegrationRequestMutation>;
130
143
  };
131
144
  export type Sdk = ReturnType<typeof getSdk>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getSdk = exports.SendIntegrationRequestDocument = exports.CreateIntegrationConnectionDocument = exports.GetConnectionStatusDocument = exports.GetIntegrationConnectionsDocument = exports.GetIntegrationConnectionDocument = exports.UserErrorFragmentDoc = exports.IntegrationConnectionFragmentDoc = void 0;
3
+ exports.getSdk = exports.SendIntegrationRequestDocument = exports.PingIntegrationConnectionDocument = exports.CreateIntegrationConnectionDocument = exports.GetConnectionStatusDocument = exports.GetIntegrationConnectionsDocument = exports.GetIntegrationConnectionDocument = exports.UserErrorFragmentDoc = exports.IntegrationConnectionFragmentDoc = void 0;
4
4
  exports.IntegrationConnectionFragmentDoc = `
5
5
  fragment IntegrationConnection on IntegrationConnection {
6
6
  id
@@ -53,6 +53,13 @@ exports.CreateIntegrationConnectionDocument = `
53
53
  }
54
54
  }
55
55
  ${exports.IntegrationConnectionFragmentDoc}`;
56
+ exports.PingIntegrationConnectionDocument = `
57
+ mutation pingIntegrationConnection($input: PingIntegrationConnectionInput!) {
58
+ pingIntegrationConnection(input: $input) {
59
+ duration
60
+ }
61
+ }
62
+ `;
56
63
  exports.SendIntegrationRequestDocument = `
57
64
  mutation sendIntegrationRequest($input: SendIntegrationRequestInput!) {
58
65
  sendIntegrationRequest(input: $input) {
@@ -75,6 +82,9 @@ function getSdk(client, withWrapper = defaultWrapper) {
75
82
  createIntegrationConnection(variables, requestHeaders) {
76
83
  return withWrapper((wrappedRequestHeaders) => client.request(exports.CreateIntegrationConnectionDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "createIntegrationConnection", "mutation");
77
84
  },
85
+ pingIntegrationConnection(variables, requestHeaders) {
86
+ return withWrapper((wrappedRequestHeaders) => client.request(exports.PingIntegrationConnectionDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "pingIntegrationConnection", "mutation");
87
+ },
78
88
  sendIntegrationRequest(variables, requestHeaders) {
79
89
  return withWrapper((wrappedRequestHeaders) => client.request(exports.SendIntegrationRequestDocument, variables, { ...requestHeaders, ...wrappedRequestHeaders }), "sendIntegrationRequest", "mutation");
80
90
  },
@@ -85,6 +85,7 @@ function generateConductorError(error) {
85
85
  // definition, we've seen it occur (e.g., attempting to access the
86
86
  // `development` environment when `ngrok` is not running locally).
87
87
  response["error"];
88
+ console.log("***********************");
88
89
  if (errorMessage !== undefined) {
89
90
  return new error_1.ConductorError({
90
91
  type: "temp",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "conductor-node",
3
- "version": "8.2.4",
3
+ "version": "8.3.0",
4
4
  "description": "Easily integrate with the entire QuickBooks Desktop API with fully-typed async TypeScript",
5
5
  "author": "Danny Nemer <hi@DannyNemer.com>",
6
6
  "license": "MIT",