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

box / box-typescript-sdk-gen / 6814673855

09 Nov 2023 04:31PM UTC coverage: 31.432% (+1.3%) from 30.127%
6814673855

Pull #41

github

web-flow
Merge 3aec2106f into 1aa8f5afc
Pull Request #41: codegen output 13ed41f186c7473db272e90659610b17

1501 of 9137 branches covered (0.0%)

Branch coverage included in aggregate %.

492 of 992 new or added lines in 75 files covered. (49.6%)

160 existing lines in 14 files now uncovered.

5124 of 11940 relevant lines covered (42.91%)

27.06 hits per line

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

67.81
/src/managers/chunkedUploads.generated.ts
1
import { serializeUploadSession } from '../schemas.generated.js';
2
import { deserializeUploadSession } from '../schemas.generated.js';
70✔
3
import { serializeClientError } from '../schemas.generated.js';
4
import { deserializeClientError } from '../schemas.generated.js';
5
import { serializeUploadedPart } from '../schemas.generated.js';
6
import { deserializeUploadedPart } from '../schemas.generated.js';
70✔
7
import { serializeUploadParts } from '../schemas.generated.js';
8
import { deserializeUploadParts } from '../schemas.generated.js';
70✔
9
import { serializeFiles } from '../schemas.generated.js';
10
import { deserializeFiles } from '../schemas.generated.js';
70✔
11
import { serializeUploadPart } from '../schemas.generated.js';
70✔
12
import { deserializeUploadPart } from '../schemas.generated.js';
70✔
13
import { Buffer } from '../utils.js';
14
import { HashName } from '../utils.js';
15
import { Iterator } from '../utils.js';
16
import { UploadSession } from '../schemas.generated.js';
17
import { ClientError } from '../schemas.generated.js';
18
import { UploadedPart } from '../schemas.generated.js';
19
import { UploadParts } from '../schemas.generated.js';
20
import { Files } from '../schemas.generated.js';
21
import { UploadPart } from '../schemas.generated.js';
22
import { Authentication } from '../auth.js';
23
import { NetworkSession } from '../network.js';
24
import { prepareParams } from '../utils.js';
70✔
25
import { toString } from '../utils.js';
70✔
26
import { ByteStream } from '../utils.js';
27
import { CancellationToken } from '../utils.js';
28
import { fetch } from '../fetch.js';
70✔
29
import { FetchOptions } from '../fetch.js';
30
import { FetchResponse } from '../fetch.js';
31
import { SerializedData } from '../json.js';
32
import { sdToJson } from '../json.js';
33
import { generateByteStreamFromBuffer } from '../utils.js';
70✔
34
import { hexToBase64 } from '../utils.js';
70✔
35
import { iterateChunks } from '../utils.js';
70✔
36
import { readByteStream } from '../utils.js';
70✔
37
import { reduceIterator } from '../utils.js';
70✔
38
import { Hash } from '../utils.js';
70✔
39
import { listConcat } from '../utils.js';
70✔
40
import { bufferLength } from '../utils.js';
70✔
41
import { sdIsEmpty } from '../json.js';
42
import { sdIsBoolean } from '../json.js';
43
import { sdIsNumber } from '../json.js';
44
import { sdIsString } from '../json.js';
45
import { sdIsList } from '../json.js';
70✔
46
import { sdIsMap } from '../json.js';
47
export interface PartAccumulator {
48
  readonly lastIndex: number;
49
  readonly parts: readonly UploadPart[];
50
  readonly fileSize: number;
51
  readonly uploadSessionId: string;
52
  readonly fileHash: Hash;
53
}
54
export interface CreateFileUploadSessionRequestBodyArg {
55
  readonly folderId: string;
56
  readonly fileSize: number;
57
  readonly fileName: string;
58
}
59
export class CreateFileUploadSessionHeadersArg {
70✔
60
  readonly extraHeaders?: {
2✔
61
    readonly [key: string]: undefined | string;
62
  } = {};
63
  constructor(
64
    fields:
65
      | Omit<CreateFileUploadSessionHeadersArg, 'extraHeaders'>
66
      | Partial<Pick<CreateFileUploadSessionHeadersArg, 'extraHeaders'>>
67
  ) {
68
    Object.assign(this, fields);
2✔
69
  }
70
}
71
export interface CreateFileUploadSessionForExistingFileRequestBodyArg {
72
  readonly fileSize: number;
73
  readonly fileName?: string;
74
}
75
export class CreateFileUploadSessionForExistingFileHeadersArg {
70✔
76
  readonly extraHeaders?: {
×
77
    readonly [key: string]: undefined | string;
78
  } = {};
79
  constructor(
80
    fields:
81
      | Omit<CreateFileUploadSessionForExistingFileHeadersArg, 'extraHeaders'>
82
      | Partial<
83
          Pick<CreateFileUploadSessionForExistingFileHeadersArg, 'extraHeaders'>
84
        >
85
  ) {
86
    Object.assign(this, fields);
×
87
  }
88
}
89
export class GetFileUploadSessionByIdHeadersArg {
70✔
90
  readonly extraHeaders?: {
2✔
91
    readonly [key: string]: undefined | string;
92
  } = {};
93
  constructor(
94
    fields:
95
      | Omit<GetFileUploadSessionByIdHeadersArg, 'extraHeaders'>
96
      | Partial<Pick<GetFileUploadSessionByIdHeadersArg, 'extraHeaders'>>
97
  ) {
98
    Object.assign(this, fields);
2✔
99
  }
100
}
101
export class UploadFilePartHeadersArg {
70✔
102
  readonly digest!: string;
103
  readonly contentRange!: string;
104
  readonly extraHeaders?: {
6✔
105
    readonly [key: string]: undefined | string;
106
  } = {};
107
  constructor(
108
    fields:
109
      | Omit<UploadFilePartHeadersArg, 'extraHeaders'>
110
      | Partial<Pick<UploadFilePartHeadersArg, 'extraHeaders'>>
111
  ) {
112
    Object.assign(this, fields);
6✔
113
  }
114
}
115
export class DeleteFileUploadSessionByIdHeadersArg {
70✔
116
  readonly extraHeaders?: {
×
117
    readonly [key: string]: undefined | string;
118
  } = {};
119
  constructor(
120
    fields:
121
      | Omit<DeleteFileUploadSessionByIdHeadersArg, 'extraHeaders'>
122
      | Partial<Pick<DeleteFileUploadSessionByIdHeadersArg, 'extraHeaders'>>
123
  ) {
124
    Object.assign(this, fields);
×
125
  }
126
}
127
export interface GetFileUploadSessionPartsQueryParamsArg {
128
  readonly offset?: number;
129
  readonly limit?: number;
130
}
131
export class GetFileUploadSessionPartsHeadersArg {
70✔
132
  readonly extraHeaders?: {
2✔
133
    readonly [key: string]: undefined | string;
134
  } = {};
135
  constructor(
136
    fields:
137
      | Omit<GetFileUploadSessionPartsHeadersArg, 'extraHeaders'>
138
      | Partial<Pick<GetFileUploadSessionPartsHeadersArg, 'extraHeaders'>>
139
  ) {
140
    Object.assign(this, fields);
2✔
141
  }
142
}
143
export interface CreateFileUploadSessionCommitRequestBodyArg {
144
  readonly parts: readonly UploadPart[];
145
}
146
export class CreateFileUploadSessionCommitHeadersArg {
70✔
147
  readonly digest!: string;
148
  readonly ifMatch?: string;
149
  readonly ifNoneMatch?: string;
150
  readonly extraHeaders?: {
2✔
151
    readonly [key: string]: undefined | string;
152
  } = {};
153
  constructor(
154
    fields:
155
      | Omit<CreateFileUploadSessionCommitHeadersArg, 'extraHeaders'>
156
      | Partial<Pick<CreateFileUploadSessionCommitHeadersArg, 'extraHeaders'>>
157
  ) {
158
    Object.assign(this, fields);
2✔
159
  }
160
}
161
export class ChunkedUploadsManager {
70✔
162
  readonly auth?: Authentication;
163
  readonly networkSession?: NetworkSession;
164
  constructor(
165
    fields: Omit<
166
      ChunkedUploadsManager,
167
      | 'createFileUploadSession'
168
      | 'createFileUploadSessionForExistingFile'
169
      | 'getFileUploadSessionById'
170
      | 'uploadFilePart'
171
      | 'deleteFileUploadSessionById'
172
      | 'getFileUploadSessionParts'
173
      | 'createFileUploadSessionCommit'
174
      | 'reducer'
175
      | 'uploadBigFile'
176
    >
177
  ) {
178
    Object.assign(this, fields);
104✔
179
  }
180
  async createFileUploadSession(
181
    requestBody: CreateFileUploadSessionRequestBodyArg,
182
    headers: CreateFileUploadSessionHeadersArg = new CreateFileUploadSessionHeadersArg(
×
183
      {}
184
    ),
185
    cancellationToken?: CancellationToken
186
  ): Promise<UploadSession> {
187
    const headersMap: {
188
      readonly [key: string]: string;
189
    } = prepareParams({ ...{}, ...headers.extraHeaders });
2✔
190
    const response: FetchResponse = (await fetch(
2✔
191
      ''.concat(
192
        'https://upload.box.com/api/2.0/files/upload_sessions'
193
      ) as string,
194
      {
195
        method: 'POST',
196
        headers: headersMap,
197
        data: serializeCreateFileUploadSessionRequestBodyArg(requestBody),
198
        contentType: 'application/json',
199
        responseFormat: 'json',
200
        auth: this.auth,
201
        networkSession: this.networkSession,
202
        cancellationToken: cancellationToken,
203
      } satisfies FetchOptions
204
    )) as FetchResponse;
205
    return deserializeUploadSession(response.data);
2✔
206
  }
207
  async createFileUploadSessionForExistingFile(
208
    fileId: string,
209
    requestBody: CreateFileUploadSessionForExistingFileRequestBodyArg,
210
    headers: CreateFileUploadSessionForExistingFileHeadersArg = new CreateFileUploadSessionForExistingFileHeadersArg(
×
211
      {}
212
    ),
213
    cancellationToken?: CancellationToken
214
  ): Promise<UploadSession> {
215
    const headersMap: {
216
      readonly [key: string]: string;
217
    } = prepareParams({ ...{}, ...headers.extraHeaders });
×
218
    const response: FetchResponse = (await fetch(
×
219
      ''.concat(
220
        'https://upload.box.com/api/2.0/files/',
221
        toString(fileId) as string,
222
        '/upload_sessions'
223
      ) as string,
224
      {
225
        method: 'POST',
226
        headers: headersMap,
227
        data: serializeCreateFileUploadSessionForExistingFileRequestBodyArg(
228
          requestBody
229
        ),
230
        contentType: 'application/json',
231
        responseFormat: 'json',
232
        auth: this.auth,
233
        networkSession: this.networkSession,
234
        cancellationToken: cancellationToken,
235
      } satisfies FetchOptions
236
    )) as FetchResponse;
NEW
237
    return deserializeUploadSession(response.data);
×
238
  }
239
  async getFileUploadSessionById(
240
    uploadSessionId: string,
241
    headers: GetFileUploadSessionByIdHeadersArg = new GetFileUploadSessionByIdHeadersArg(
×
242
      {}
243
    ),
244
    cancellationToken?: CancellationToken
245
  ): Promise<UploadSession> {
246
    const headersMap: {
247
      readonly [key: string]: string;
248
    } = prepareParams({ ...{}, ...headers.extraHeaders });
2✔
249
    const response: FetchResponse = (await fetch(
2✔
250
      ''.concat(
251
        'https://upload.box.com/api/2.0/files/upload_sessions/',
252
        toString(uploadSessionId) as string
253
      ) as string,
254
      {
255
        method: 'GET',
256
        headers: headersMap,
257
        responseFormat: 'json',
258
        auth: this.auth,
259
        networkSession: this.networkSession,
260
        cancellationToken: cancellationToken,
261
      } satisfies FetchOptions
262
    )) as FetchResponse;
263
    return deserializeUploadSession(response.data);
2✔
264
  }
265
  async uploadFilePart(
266
    uploadSessionId: string,
267
    requestBody: ByteStream,
268
    headers: UploadFilePartHeadersArg,
269
    cancellationToken?: CancellationToken
270
  ): Promise<UploadedPart> {
271
    const headersMap: {
272
      readonly [key: string]: string;
273
    } = prepareParams({
6✔
274
      ...{
275
        ['digest']: toString(headers.digest) as string,
276
        ['content-range']: toString(headers.contentRange) as string,
277
      },
278
      ...headers.extraHeaders,
279
    });
280
    const response: FetchResponse = (await fetch(
6✔
281
      ''.concat(
282
        'https://upload.box.com/api/2.0/files/upload_sessions/',
283
        toString(uploadSessionId) as string
284
      ) as string,
285
      {
286
        method: 'PUT',
287
        headers: headersMap,
288
        fileStream: requestBody,
289
        contentType: 'application/octet-stream',
290
        responseFormat: 'json',
291
        auth: this.auth,
292
        networkSession: this.networkSession,
293
        cancellationToken: cancellationToken,
294
      } satisfies FetchOptions
295
    )) as FetchResponse;
296
    return deserializeUploadedPart(response.data);
6✔
297
  }
298
  async deleteFileUploadSessionById(
299
    uploadSessionId: string,
300
    headers: DeleteFileUploadSessionByIdHeadersArg = new DeleteFileUploadSessionByIdHeadersArg(
×
301
      {}
302
    ),
303
    cancellationToken?: CancellationToken
304
  ): Promise<undefined> {
305
    const headersMap: {
306
      readonly [key: string]: string;
307
    } = prepareParams({ ...{}, ...headers.extraHeaders });
×
308
    const response: FetchResponse = (await fetch(
×
309
      ''.concat(
310
        'https://upload.box.com/api/2.0/files/upload_sessions/',
311
        toString(uploadSessionId) as string
312
      ) as string,
313
      {
314
        method: 'DELETE',
315
        headers: headersMap,
316
        responseFormat: void 0,
317
        auth: this.auth,
318
        networkSession: this.networkSession,
319
        cancellationToken: cancellationToken,
320
      } satisfies FetchOptions
321
    )) as FetchResponse;
322
    return void 0;
×
323
  }
324
  async getFileUploadSessionParts(
325
    uploadSessionId: string,
326
    queryParams: GetFileUploadSessionPartsQueryParamsArg = {} satisfies GetFileUploadSessionPartsQueryParamsArg,
×
327
    headers: GetFileUploadSessionPartsHeadersArg = new GetFileUploadSessionPartsHeadersArg(
×
328
      {}
329
    ),
330
    cancellationToken?: CancellationToken
331
  ): Promise<UploadParts> {
332
    const queryParamsMap: {
333
      readonly [key: string]: string;
334
    } = prepareParams({
2✔
335
      ['offset']: toString(queryParams.offset) as string,
336
      ['limit']: toString(queryParams.limit) as string,
337
    });
338
    const headersMap: {
339
      readonly [key: string]: string;
340
    } = prepareParams({ ...{}, ...headers.extraHeaders });
2✔
341
    const response: FetchResponse = (await fetch(
2✔
342
      ''.concat(
343
        'https://upload.box.com/api/2.0/files/upload_sessions/',
344
        toString(uploadSessionId) as string,
345
        '/parts'
346
      ) as string,
347
      {
348
        method: 'GET',
349
        params: queryParamsMap,
350
        headers: headersMap,
351
        responseFormat: 'json',
352
        auth: this.auth,
353
        networkSession: this.networkSession,
354
        cancellationToken: cancellationToken,
355
      } satisfies FetchOptions
356
    )) as FetchResponse;
357
    return deserializeUploadParts(response.data);
2✔
358
  }
359
  async createFileUploadSessionCommit(
360
    uploadSessionId: string,
361
    requestBody: CreateFileUploadSessionCommitRequestBodyArg,
362
    headers: CreateFileUploadSessionCommitHeadersArg,
363
    cancellationToken?: CancellationToken
364
  ): Promise<Files> {
365
    const headersMap: {
366
      readonly [key: string]: string;
367
    } = prepareParams({
2✔
368
      ...{
369
        ['digest']: toString(headers.digest) as string,
370
        ['if-match']: toString(headers.ifMatch) as string,
371
        ['if-none-match']: toString(headers.ifNoneMatch) as string,
372
      },
373
      ...headers.extraHeaders,
374
    });
375
    const response: FetchResponse = (await fetch(
2✔
376
      ''.concat(
377
        'https://upload.box.com/api/2.0/files/upload_sessions/',
378
        toString(uploadSessionId) as string,
379
        '/commit'
380
      ) as string,
381
      {
382
        method: 'POST',
383
        headers: headersMap,
384
        data: serializeCreateFileUploadSessionCommitRequestBodyArg(requestBody),
385
        contentType: 'application/json',
386
        responseFormat: 'json',
387
        auth: this.auth,
388
        networkSession: this.networkSession,
389
        cancellationToken: cancellationToken,
390
      } satisfies FetchOptions
391
    )) as FetchResponse;
392
    return deserializeFiles(response.data);
2✔
393
  }
394
  async reducer(acc: PartAccumulator, chunk: ByteStream): Promise<any> {
395
    const lastIndex: number = acc.lastIndex;
6✔
396
    const parts: readonly UploadPart[] = acc.parts;
6✔
397
    const chunkBuffer: Buffer = await readByteStream(chunk);
6✔
398
    const hash: Hash = new Hash({ algorithm: 'sha1' as HashName });
6✔
399
    hash.updateHash(chunkBuffer);
6✔
400
    const sha1: string = await hash.digestHash('base64');
6✔
401
    const digest: string = ''.concat('sha=', sha1) as string;
6✔
402
    const chunkSize: number = bufferLength(chunkBuffer);
6✔
403
    const bytesStart: number = lastIndex + 1;
6✔
404
    const bytesEnd: number = lastIndex + chunkSize;
6✔
405
    const contentRange: string = ''.concat(
6✔
406
      'bytes ',
407
      toString(bytesStart) as string,
408
      '-',
409
      toString(bytesEnd) as string,
410
      '/',
411
      toString(acc.fileSize) as string
412
    ) as string;
413
    const uploadedPart: UploadedPart = await this.uploadFilePart(
6✔
414
      acc.uploadSessionId,
415
      generateByteStreamFromBuffer(chunkBuffer),
416
      new UploadFilePartHeadersArg({
417
        digest: digest,
418
        contentRange: contentRange,
419
      })
420
    );
421
    const part: UploadPart = uploadedPart.part!;
6✔
422
    const partSha1: string = hexToBase64(part.sha1!);
6✔
423
    if (!(partSha1 == sha1)) {
6!
424
      throw 'Assertion failed';
×
425
    }
426
    if (!(part.size == chunkSize)) {
6!
427
      throw 'Assertion failed';
×
428
    }
429
    if (!(part.offset == bytesStart)) {
6!
430
      throw 'Assertion failed';
×
431
    }
432
    acc.fileHash.updateHash(chunkBuffer);
6✔
433
    return {
6✔
434
      lastIndex: bytesEnd,
435
      parts: listConcat(parts, [part]),
436
      fileSize: acc.fileSize,
437
      uploadSessionId: acc.uploadSessionId,
438
      fileHash: acc.fileHash,
439
    } satisfies PartAccumulator;
440
  }
441
  async uploadBigFile(
442
    file: ByteStream,
443
    fileName: string,
444
    fileSize: number,
445
    parentFolderId: string,
446
    cancellationToken?: CancellationToken
447
  ): Promise<any> {
448
    const uploadSession: UploadSession = await this.createFileUploadSession(
2✔
449
      {
450
        fileName: fileName,
451
        fileSize: fileSize,
452
        folderId: parentFolderId,
453
      } satisfies CreateFileUploadSessionRequestBodyArg,
454
      new CreateFileUploadSessionHeadersArg({}),
455
      cancellationToken
456
    );
457
    const uploadSessionId: string = uploadSession.id!;
2✔
458
    const partSize: number = uploadSession.partSize!;
2✔
459
    const totalParts: number = uploadSession.totalParts!;
2✔
460
    if (!(partSize * totalParts >= fileSize)) {
2!
461
      throw 'Assertion failed';
×
462
    }
463
    if (!(uploadSession.numPartsProcessed == 0)) {
2!
464
      throw 'Assertion failed';
×
465
    }
466
    const fileHash: Hash = new Hash({ algorithm: 'sha1' as HashName });
2✔
467
    const chunksIterator: Iterator = iterateChunks(file, partSize);
2✔
468
    const results: PartAccumulator = await reduceIterator(
2✔
469
      chunksIterator,
470
      this.reducer.bind(this),
471
      {
472
        lastIndex: -1,
473
        parts: [],
474
        fileSize: fileSize,
475
        uploadSessionId: uploadSessionId,
476
        fileHash: fileHash,
477
      } satisfies PartAccumulator
478
    );
479
    const parts: readonly UploadPart[] = results.parts;
2✔
480
    const processedSessionParts: UploadParts =
481
      await this.getFileUploadSessionParts(
2✔
482
        uploadSessionId,
483
        {} satisfies GetFileUploadSessionPartsQueryParamsArg,
484
        new GetFileUploadSessionPartsHeadersArg({}),
485
        cancellationToken
486
      );
487
    if (!(processedSessionParts.totalCount! == totalParts)) {
2!
488
      throw 'Assertion failed';
×
489
    }
490
    const processedSession: UploadSession = await this.getFileUploadSessionById(
2✔
491
      uploadSessionId,
492
      new GetFileUploadSessionByIdHeadersArg({}),
493
      cancellationToken
494
    );
495
    if (!(processedSession.numPartsProcessed == totalParts)) {
2!
496
      throw 'Assertion failed';
×
497
    }
498
    const sha1: string = await fileHash.digestHash('base64');
2✔
499
    const digest: string = ''.concat('sha=', sha1) as string;
2✔
500
    const committedSession: Files = await this.createFileUploadSessionCommit(
2✔
501
      uploadSessionId,
502
      { parts: parts } satisfies CreateFileUploadSessionCommitRequestBodyArg,
503
      new CreateFileUploadSessionCommitHeadersArg({ digest: digest }),
504
      cancellationToken
505
    );
506
    return committedSession.entries![0];
2✔
507
  }
508
}
509
export function serializeCreateFileUploadSessionRequestBodyArg(
70✔
510
  val: CreateFileUploadSessionRequestBodyArg
511
): SerializedData {
512
  return {
2✔
513
    ['folder_id']: val.folderId,
514
    ['file_size']: val.fileSize,
515
    ['file_name']: val.fileName,
516
  };
517
}
518
export function deserializeCreateFileUploadSessionRequestBodyArg(
70✔
519
  val: any
520
): CreateFileUploadSessionRequestBodyArg {
521
  const folderId: string = val.folder_id;
×
522
  const fileSize: number = val.file_size;
×
523
  const fileName: string = val.file_name;
×
524
  return {
×
525
    folderId: folderId,
526
    fileSize: fileSize,
527
    fileName: fileName,
528
  } satisfies CreateFileUploadSessionRequestBodyArg;
529
}
530
export function serializeCreateFileUploadSessionForExistingFileRequestBodyArg(
70✔
531
  val: CreateFileUploadSessionForExistingFileRequestBodyArg
532
): SerializedData {
533
  return {
×
534
    ['file_size']: val.fileSize,
535
    ['file_name']: val.fileName == void 0 ? void 0 : val.fileName,
×
536
  };
537
}
538
export function deserializeCreateFileUploadSessionForExistingFileRequestBodyArg(
70✔
539
  val: any
540
): CreateFileUploadSessionForExistingFileRequestBodyArg {
541
  const fileSize: number = val.file_size;
×
542
  const fileName: undefined | string =
543
    val.file_name == void 0 ? void 0 : val.file_name;
×
544
  return {
×
545
    fileSize: fileSize,
546
    fileName: fileName,
547
  } satisfies CreateFileUploadSessionForExistingFileRequestBodyArg;
548
}
549
export function serializeCreateFileUploadSessionCommitRequestBodyArg(
70✔
550
  val: CreateFileUploadSessionCommitRequestBodyArg
551
): SerializedData {
552
  return {
2✔
553
    ['parts']: val.parts.map(function (item: UploadPart): any {
554
      return serializeUploadPart(item);
6✔
555
    }) as readonly any[],
556
  };
557
}
558
export function deserializeCreateFileUploadSessionCommitRequestBodyArg(
70✔
559
  val: any
560
): CreateFileUploadSessionCommitRequestBodyArg {
NEW
561
  const parts: readonly UploadPart[] = sdIsList(val.parts)
×
562
    ? (val.parts.map(function (itm: SerializedData): any {
UNCOV
563
        return deserializeUploadPart(itm);
×
564
      }) as readonly any[])
565
    : [];
566
  return { parts: parts } satisfies CreateFileUploadSessionCommitRequestBodyArg;
×
567
}
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

© 2025 Coveralls, Inc