• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

covenanttechnologysolutions / connectwise-rest / 24438413754

15 Apr 2026 05:40AM UTC coverage: 43.089%. First build
24438413754

push

github

web-flow
Merge pull request #55 from covenanttechnologysolutions/v2

Regenerate for 2025 schemas, update build pipeline

22 of 129 branches covered (17.05%)

Branch coverage included in aggregate %.

117 of 146 new or added lines in 6 files covered. (80.14%)

190 of 363 relevant lines covered (52.34%)

0.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

12.0
/src/BaseAPI.ts
1
import { RequestOptions } from './types'
2
import promiseRetry from 'promise-retry'
1✔
3
import Manage, { ManageConfig } from './Manage'
4
import Automate, { AutomateConfig } from './Automate'
5
import { CommonParameters } from './AutomateAPI'
6

7
/**
8
 * Build a `FormData` from a plain object for multipart uploads. File fields can
9
 * be any value FormData.append accepts natively (Blob, File, Buffer, Uint8Array,
10
 * ReadStream under the Node ponyfill). Primitive values are stringified.
11
 *
12
 * @public
13
 */
14
export function toFormData(input: Record<string, unknown>): FormData {
1✔
NEW
15
  const fd = new FormData()
×
NEW
16
  for (const [k, v] of Object.entries(input)) {
×
NEW
17
    if (v === undefined || v === null) {
×
NEW
18
      continue
×
19
    }
NEW
20
    if (v instanceof Blob) {
×
NEW
21
      fd.append(k, v)
×
NEW
22
    } else if (typeof v === 'string') {
×
NEW
23
      fd.append(k, v)
×
NEW
24
    } else if (typeof v === 'number' || typeof v === 'boolean' || typeof v === 'bigint') {
×
NEW
25
      fd.append(k, String(v))
×
26
    } else {
27
      // Buffer, Uint8Array, File, ReadStream all accepted by FormData.append
28
      // through Blob-like coercion in modern runtimes.
NEW
29
      fd.append(k, v as Blob)
×
30
    }
31
  }
NEW
32
  return fd
×
33
}
34

35
/**
36
 * Base class for generated Automate API sections. Holds a shared `Automate`
37
 * client reference so every sub-API uses the same axios instance and auth state.
38
 * @public
39
 */
40
export class AutomateBaseAPI {
1✔
41
  readonly #client: Automate
42

43
  /**
44
   * Sub-APIs are instantiated by the AutomateAPI aggregator's lazy getters.
45
   * Consumers should not call this directly.
46
   * @internal
47
   */
48
  constructor(client: Automate) {
49
    this.#client = client
20✔
50
  }
51

52
  protected request<T = unknown>(args: RequestOptions): Promise<T> {
NEW
53
    return this.#client.request(args) as Promise<T>
×
54
  }
55
}
56

57
/**
58
 * Base class for generated Manage API sections. Holds a shared `Manage`
59
 * client reference so every sub-API uses the same axios instance and auth state.
60
 * @public
61
 */
62
export class ManageBaseAPI {
1✔
63
  readonly #client: Manage
64

65
  /**
66
   * Sub-APIs are instantiated by the ManageAPI aggregator's lazy getters.
67
   * Consumers should not call this directly.
68
   * @internal
69
   */
70
  constructor(client: Manage) {
71
    this.#client = client
12✔
72
  }
73

74
  protected request<T = unknown>(args: RequestOptions): Promise<T> {
NEW
75
    return this.#client.request(args) as Promise<T>
×
76
  }
77
}
78

79
/**
80
 * curried request function
81
 * @internal
82
 */
83
export const makeRequest =
1✔
84
  ({
1✔
85
    config,
86
    api,
87
    thisObj,
88
  }: {
89
    config: ManageConfig | AutomateConfig
90
    api: (args: RequestOptions) => Promise<unknown>
91
    thisObj: InstanceType<typeof Automate | typeof Manage>
92
  }): ((args: RequestOptions) => Promise<unknown>) =>
93
  ({ path, method = 'get', params, data }: RequestOptions): Promise<unknown> => {
2!
94
    const retryCodes = ['ECONNRESET', 'ETIMEDOUT', 'ESOCKETTIMEDOUT']
×
95
    const boundApi = api.bind(thisObj)
×
96

97
    if (!path) {
×
98
      throw new Error('path must be defined.')
×
99
    }
100

101
    let startTime = Date.now()
×
102
    const { retry, retryOptions, logger } = config
×
103

104
    if (!retry) {
×
105
      return boundApi({ path, method, params, data })
×
106
        .then((result: any) => {
107
          logger(
×
108
            'info',
109
            `${method} ${path} ${Date.now() - startTime}ms params=${JSON.stringify(params)}`,
110
          )
111
          return result
×
112
        })
113
        .catch((error: Record<string, unknown> | undefined) => {
114
          logger(
×
115
            'error',
116
            `${method} ${path} ${Date.now() - startTime}ms params=${JSON.stringify(params)}`,
117
            error,
118
          )
119
          throw error
×
120
        })
121
    } else {
122
      return promiseRetry(retryOptions, (retry, number) => {
×
123
        return boundApi({ path, method, params, data }).catch((error) => {
×
124
          logger(
×
125
            'warn',
126
            `${method} ${path} ${Date.now() - startTime}ms error occurred: ${
127
              error.code
128
            }, retry=${number}, params=${JSON.stringify(params)}`,
129
          )
130
          startTime = Date.now()
×
131
          if (retryCodes.includes(error.code)) {
×
132
            return retry(error)
×
133
          }
134
          throw error
×
135
        })
136
      })
137
        .then((result) => {
138
          logger(
×
139
            'info',
140
            `${method} ${path} ${Date.now() - startTime}ms params=${JSON.stringify(params)}`,
141
          )
142

143
          return result
×
144
        })
145
        .catch((error) => {
146
          logger(
×
147
            'error',
148
            `${method} ${path} ${Date.now() - startTime}ms error occurred: ${
149
              error.code
150
            }, params=${JSON.stringify(params)}`,
151
            error,
152
          )
153
          throw error
×
154
        })
155
    }
156
  }
157

158
export interface PaginationConfig {
159
  thisObj: InstanceType<typeof Automate | typeof Manage>
160
}
161

162
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
163
export type PaginationApiMethod = Function
164

165
export type PaginationOptions = {
166
  pageSize?: number
167
  startPage?: number
168
}
169

170
/**
171
 * curried paginate function
172
 * @internal
173
 */
174
export const makePaginate =
1✔
175
  ({ thisObj }: PaginationConfig) =>
1✔
176
  async (
2✔
177
    apiMethod: PaginationApiMethod,
178
    paginateArgs: PaginationOptions = {},
×
179
    ...methodArgs: Record<string, unknown>[]
180
  ): Promise<unknown[]> => {
181
    const { startPage = 1, pageSize = 1000 } = paginateArgs
×
182

NEW
183
    let results: unknown[] = []
×
NEW
184
    let page = startPage
×
185

NEW
186
    if (startPage === undefined || startPage < 1) {
×
NEW
187
      page = 1
×
188
    }
189

NEW
190
    while (true) {
×
NEW
191
      const pageResults = await getPage(apiMethod, methodArgs, thisObj, page++, pageSize)
×
192
      // complete page returned, loop again
NEW
193
      if (Array.isArray(pageResults) && pageResults.length > 0) {
×
NEW
194
        results = [...results, ...pageResults]
×
NEW
195
        if (pageResults.length !== pageSize) {
×
196
          // incomplete page, there are no more pages
NEW
197
          break
×
198
        }
199
      } else {
200
        // no results returned, this is the last page, previous page was full
NEW
201
        break
×
202
      }
203
    }
204

NEW
205
    return results
×
206
  }
207

208
/**
209
 * @internal
210
 */
211
function getPage(
212
  apiMethod: PaginationApiMethod,
213
  methodArgs: Record<string, unknown>[],
214
  thisObj: InstanceType<typeof Automate | typeof Manage>,
215
  page = 1,
×
216
  pageSize = 1000,
×
217
): Promise<unknown[]> {
218
  // Javascript Function.length returns number of non-default values
219
  // the method args will always be greater than the api method args
220
  // due to this, if params is not passed in, even as an empty object,
221
  // we need to throw an error
222
  if (methodArgs.length < apiMethod.length) {
×
223
    throw new Error(
×
224
      `CommonParams must be passed in for pagination to work properly for function ${apiMethod.name}`,
225
    )
226
  }
227

228
  // look for CommonParams and inject page and pageSize
229
  const commonParams = <CommonParameters>methodArgs.pop()
×
230
  commonParams.page = page
×
231
  commonParams.pageSize = pageSize
×
232

233
  methodArgs.push(commonParams)
×
234

235
  return apiMethod.apply(thisObj, methodArgs)
×
236
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc