-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathapi-client.ts
More file actions
201 lines (180 loc) · 7.38 KB
/
Copy pathapi-client.ts
File metadata and controls
201 lines (180 loc) · 7.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
import { GraphQLClient, ClientError } from 'graphql-request';
import { ApiVersionType, DEFAULT_VERSION, QueryVariables } from './constants/index';
import { Sdk, getSdk } from './generated/sdk';
import pkg from '../package.json';
import { getApiEndpoint } from './shared/get-api-endpoint';
import { GraphQLClientResponse, RequestConfig } from 'graphql-request/build/esm/types';
export { ClientError };
export interface ApiClientConfig {
token: string;
apiVersion?: string;
endpoint?: string;
requestConfig?: RequestConfig;
}
export interface RequestOptions {
versionOverride?: string;
timeout?: number;
}
/**
* The `ApiClient` class provides a structured way to interact with the Monday.com API,
* handling GraphQL requests with configurable API versioning.
*
* This class is designed to be initialized with an authentication token and an optional
* API version, setting up the necessary headers for all subsequent API requests.
*/
export class ApiClient {
private readonly token: string;
private readonly defaultApiVersion: ApiVersionType;
private readonly defaultEndpoint?: string;
private readonly requestConfig?: RequestConfig;
public readonly operations: Sdk;
/**
* Constructs a new `ApiClient` instance, storing configuration for dynamic client creation.
*
* @param {ApiClientConfig} config - Configuration for the API client.
* Requires `token`, and optionally includes `apiVersion` and `requestConfig`.
*/
constructor(config: ApiClientConfig) {
const { token, apiVersion = DEFAULT_VERSION, endpoint, requestConfig = {} } = config;
if (!this.isValidApiVersion(apiVersion)) {
throw new Error(
"Invalid API version format. Expected format is 'yyyy-mm' with month as one of '01', '04', '07', or '10'.",
);
}
this.token = token;
this.defaultApiVersion = apiVersion;
this.defaultEndpoint = endpoint;
this.requestConfig = requestConfig;
// Create operations using a default client for backward compatibility
const defaultClient = this.createClient();
this.operations = getSdk(defaultClient);
}
/**
* Creates a GraphQL client with the specified options
*
* @param {RequestOptions} [options] - Optional request configuration
* @returns {GraphQLClient} - Configured GraphQL client
*/
private createClient(options?: RequestOptions): GraphQLClient {
const { versionOverride } = options || {};
const apiVersionToUse = versionOverride ?? this.defaultApiVersion;
if (versionOverride && !this.isValidApiVersion(versionOverride)) {
throw new Error(
"Invalid API version format. Expected format is 'yyyy-mm' with month as one of '01', '04', '07', or '10'.",
);
}
const endpoint = getApiEndpoint(this.defaultEndpoint);
const defaultHeaders = {
'Content-Type': 'application/json',
Authorization: this.token,
'API-Version': apiVersionToUse,
'Api-Sdk-Version': pkg.version,
};
const mergedHeaders = {
...defaultHeaders,
...(this.requestConfig?.headers || {}),
};
return new GraphQLClient(endpoint, {
...this.requestConfig,
headers: mergedHeaders,
});
}
/**
* Performs a GraphQL query or mutation to the Monday.com API using a dynamically created
* GraphQL client. This method is asynchronous and returns a promise that resolves
* with the query result.
*
* @param {string} query - The GraphQL query or mutation string.
* @param {QueryVariables} [variables] - An optional object containing variables for the query.
* `QueryVariables` is a type alias for `Record<string, any>`, allowing specification
* of key-value pairs where the value can be any type. This parameter is used to provide
* dynamic values in the query or mutation.
* @param {RequestOptions} [options] - Optional request configuration including version override and timeout.
* @returns {Promise<T>} A promise that resolves with the result of the query or mutation.
* @template T The expected type of the query or mutation result.
* @throws {Error} Throws an error if the request times out before receiving a response.
*/
public request = async <T>(query: string, variables?: QueryVariables, options?: RequestOptions): Promise<T> => {
const client = this.createClient(options);
const { timeout } = options || {};
if (!timeout) {
return client.request<T>(query, variables);
}
return this.requestWithTimeout<T>(
(signal) =>
client.request<T>({
document: query,
variables,
signal,
}),
timeout,
);
};
/**
* Performs a raw GraphQL query or mutation to the Monday.com API using a dynamically created
* GraphQL client. This method is asynchronous and returns a promise that resolves
* with the query result.
*
* The result will be in the raw format: data, errors, extensions.
*
* @param {string} query - The GraphQL query or mutation string.
* @param {QueryVariables} [variables] - An optional object containing variables for the query.
* `QueryVariables` is a type alias for `Record<string, any>`, allowing specification
* of key-value pairs where the value can be any type. This parameter is used to provide
* dynamic values in the query or mutation.
* @param {RequestOptions} [options] - Optional request configuration including version override and timeout.
* @returns {Promise<T>} A promise that resolves with the result of the query or mutation.
* @template T The expected type of the query or mutation result.
* @throws {Error} Throws an error if the request times out before receiving a response.
*/
public rawRequest = async <T>(
query: string,
variables?: QueryVariables,
options?: RequestOptions,
): Promise<GraphQLClientResponse<T>> => {
const client = this.createClient(options);
const { timeout } = options || {};
if (!timeout) {
return client.rawRequest<T>(query, variables);
}
return this.requestWithTimeout<GraphQLClientResponse<T>>(
(signal) =>
client.rawRequest<T>({
query,
variables,
signal,
}),
timeout,
);
};
/**
* Executes a request with timeout handling.
*
* @param {Function} requestExecutor - A function that performs the actual request, receiving an AbortSignal.
* @param {number} timeout - The timeout duration in milliseconds.
* @returns {Promise<T>} A promise that resolves with the result of the request.
* @template T The expected type of the request result.
* @throws {Error} Throws an error if the request times out before receiving a response.
*/
private async requestWithTimeout<T>(requestExecutor: (signal: AbortSignal) => Promise<T>, timeout: number): Promise<T> {
const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, timeout);
try {
const result = await requestExecutor(controller.signal);
return result;
} finally {
clearTimeout(timeoutId);
}
}
/**
* Validates the API version format (yyyy-mm), restricting mm to 01, 04, 07, or 10.
*
* @param {string} version - The API version string to validate.
* @returns {boolean} - Returns true if the version matches yyyy-mm format with allowed months.
*/
private isValidApiVersion(version: string): boolean {
return version === 'dev' || /^\d{4}-(01|04|07|10)$/.test(version);
}
}