@rbaileysr/zephyr-managed-api 1.0.1 → 1.1.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.
Files changed (61) hide show
  1. package/README.md +189 -1
  2. package/dist/README.md +865 -0
  3. package/dist/error-strategy.d.ts +3 -0
  4. package/dist/error-strategy.d.ts.map +1 -1
  5. package/dist/error-strategy.js +3 -0
  6. package/dist/groups/All.d.ts +3 -0
  7. package/dist/groups/All.d.ts.map +1 -1
  8. package/dist/groups/All.js +2 -6
  9. package/dist/groups/Automation.d.ts +3 -0
  10. package/dist/groups/Automation.d.ts.map +1 -1
  11. package/dist/groups/Automation.js +2 -3
  12. package/dist/groups/Environment.d.ts +3 -0
  13. package/dist/groups/Environment.d.ts.map +1 -1
  14. package/dist/groups/Environment.js +2 -3
  15. package/dist/groups/Folder.d.ts +3 -0
  16. package/dist/groups/Folder.d.ts.map +1 -1
  17. package/dist/groups/Folder.js +2 -3
  18. package/dist/groups/IssueLink.d.ts +3 -0
  19. package/dist/groups/IssueLink.d.ts.map +1 -1
  20. package/dist/groups/IssueLink.js +2 -3
  21. package/dist/groups/Link.d.ts +3 -0
  22. package/dist/groups/Link.d.ts.map +1 -1
  23. package/dist/groups/Link.js +2 -3
  24. package/dist/groups/Priority.d.ts +3 -0
  25. package/dist/groups/Priority.d.ts.map +1 -1
  26. package/dist/groups/Priority.js +2 -3
  27. package/dist/groups/Private.d.ts +89 -0
  28. package/dist/groups/Private.d.ts.map +1 -0
  29. package/dist/groups/Private.js +221 -0
  30. package/dist/groups/Project.d.ts +3 -0
  31. package/dist/groups/Project.d.ts.map +1 -1
  32. package/dist/groups/Project.js +2 -3
  33. package/dist/groups/Status.d.ts +3 -0
  34. package/dist/groups/Status.d.ts.map +1 -1
  35. package/dist/groups/Status.js +2 -3
  36. package/dist/groups/TestCase.d.ts +3 -0
  37. package/dist/groups/TestCase.d.ts.map +1 -1
  38. package/dist/groups/TestCase.js +2 -3
  39. package/dist/groups/TestCycle.d.ts +3 -0
  40. package/dist/groups/TestCycle.d.ts.map +1 -1
  41. package/dist/groups/TestCycle.js +2 -3
  42. package/dist/groups/TestExecution.d.ts +3 -0
  43. package/dist/groups/TestExecution.d.ts.map +1 -1
  44. package/dist/groups/TestExecution.js +2 -3
  45. package/dist/groups/TestPlan.d.ts +3 -0
  46. package/dist/groups/TestPlan.d.ts.map +1 -1
  47. package/dist/groups/TestPlan.js +2 -3
  48. package/dist/index.d.ts +7 -2
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +8 -2
  51. package/dist/package.json +54 -0
  52. package/dist/types.d.ts +39 -0
  53. package/dist/types.d.ts.map +1 -1
  54. package/dist/types.js +3 -0
  55. package/dist/utils-api-call.d.ts +3 -0
  56. package/dist/utils-api-call.d.ts.map +1 -1
  57. package/dist/utils-api-call.js +3 -0
  58. package/dist/utils.d.ts +3 -0
  59. package/dist/utils.d.ts.map +1 -1
  60. package/dist/utils.js +3 -0
  61. package/package.json +4 -4
package/dist/README.md ADDED
@@ -0,0 +1,865 @@
1
+ # Zephyr Managed API
2
+
3
+ A comprehensive Managed API wrapper for Zephyr Cloud REST API v2, providing type-safe, hierarchical access to all Zephyr API endpoints.
4
+
5
+ > **⚠️ Important: ScriptRunner Connect Runtime Only**
6
+ >
7
+ > This package is specifically designed for **ScriptRunner Connect's custom runtime** and will **NOT work in standard Node.js projects**. It uses web standards-based APIs (fetch, etc.) and ES modules, making it compatible with ScriptRunner Connect's runtime environment.
8
+
9
+ ## Overview
10
+
11
+ This Managed API wrapper provides a clean, type-safe interface to interact with the Zephyr Cloud API. It follows the same hierarchical pattern as other ScriptRunner Connect Managed APIs, making it easy to use and consistent with the platform's conventions.
12
+
13
+ ## Features
14
+
15
+ - **Type-Safe**: Full TypeScript support with IntelliSense
16
+ - **Hierarchical Structure**: Organized by resource type (TestCase, TestCycle, TestPlan, etc.)
17
+ - **Comprehensive Coverage**: Supports all Zephyr Cloud REST API v2 endpoints
18
+ - **Error Handling**: Built-in error parsing and handling using Commons Core error types
19
+ - **Pagination Support**: Both offset-based and cursor-based pagination
20
+ - **Flexible Authentication**: Supports OAuth tokens and ScriptRunner Connect API Connections (base URL configured in Generic Connector)
21
+ - **Custom Fields**: Full support for dynamic custom fields
22
+ - **Web App Compatible**: Works seamlessly in ScriptRunner Connect web app
23
+ - **Single Import**: Convenient default export for all utilities and types
24
+
25
+ ## Installation
26
+
27
+ ### NPM Package
28
+
29
+ ```bash
30
+ npm install @rbaileysr/zephyr-managed-api
31
+ ```
32
+
33
+ **Important:** After installing via NPM, you must also add the package through ScriptRunner Connect's Package Manager in the web UI. The package will then be available in your workspace.
34
+
35
+ ### ScriptRunner Connect Workspace Setup
36
+
37
+ 1. **Add Package via Web UI:**
38
+ - Go to Package Manager in ScriptRunner Connect web UI
39
+ - Click "Add Package"
40
+ - Enter: `@rbaileysr/zephyr-managed-api`
41
+ - Click Add/Save
42
+
43
+ 2. **Sync Workspace Files:**
44
+ - Download latest workspace files from SFTP server
45
+ - Or use `SFTP: Sync Remote → Local` if using VS Code + SFTP
46
+
47
+ 3. **Install Dependencies:**
48
+ ```bash
49
+ npm install
50
+ ```
51
+
52
+ 4. **Use in Scripts:**
53
+ ```typescript
54
+ // Single import (recommended) - everything in one import
55
+ import Zephyr from '@rbaileysr/zephyr-managed-api';
56
+ import ZephyrApiConnection from '../api/zephyr';
57
+
58
+ // Using API Connection (recommended for ScriptRunner Connect)
59
+ // Base URL is configured in the Generic Connector
60
+ const api = Zephyr.createZephyrApi(ZephyrApiConnection);
61
+
62
+ // Access utilities and error types from the same import
63
+ const pages = await Zephyr.getAllPages(...);
64
+ const errorStrategy = new Zephyr.ErrorStrategyBuilder()...;
65
+ ```
66
+
67
+ **Alternative - Named Imports:**
68
+ ```typescript
69
+ // Named imports (if you prefer)
70
+ import { createZephyrApi, getAllPages, ErrorStrategyBuilder } from '@rbaileysr/zephyr-managed-api';
71
+ ```
72
+
73
+ ## Usage
74
+
75
+ ### Basic Examples
76
+
77
+ #### Using API Connection (ScriptRunner Connect) - Single Import
78
+
79
+ ```typescript
80
+ // Single import for everything
81
+ import Zephyr from '@rbaileysr/zephyr-managed-api';
82
+ import ZephyrApiConnection from '../api/zephyr';
83
+
84
+ // Create API instance (base URL configured in Generic Connector)
85
+ const api = Zephyr.createZephyrApi(ZephyrApiConnection);
86
+
87
+ // Get a test case
88
+ const testCase = await api.TestCase.getTestCase({ testCaseKey: 'PROJ-T1' });
89
+ console.log(`Test case: ${testCase.name}`);
90
+ ```
91
+
92
+ #### Using API Connection - Named Imports
93
+
94
+ ```typescript
95
+ // Named imports (alternative)
96
+ import { createZephyrApi } from '@rbaileysr/zephyr-managed-api';
97
+ import ZephyrApiConnection from '../api/zephyr';
98
+
99
+ const api = createZephyrApi(ZephyrApiConnection);
100
+ const testCase = await api.TestCase.getTestCase({ testCaseKey: 'PROJ-T1' });
101
+ ```
102
+
103
+ #### Using OAuth Token
104
+
105
+ ```typescript
106
+ import Zephyr from '@rbaileysr/zephyr-managed-api';
107
+
108
+ // OAuth token with full base URL (US region)
109
+ const api = Zephyr.createZephyrApi(
110
+ 'your-oauth-token-here',
111
+ 'https://api.zephyrscale.smartbear.com/v2'
112
+ );
113
+
114
+ // OAuth token with full base URL (EU region)
115
+ const apiEU = Zephyr.createZephyrApi(
116
+ 'your-oauth-token-here',
117
+ 'https://eu.api.zephyrscale.smartbear.com/v2'
118
+ );
119
+
120
+ // List test cases
121
+ const testCases = await api.TestCase.listTestCases({
122
+ projectKey: 'PROJ',
123
+ maxResults: 10,
124
+ startAt: 0
125
+ });
126
+ console.log(`Found ${testCases.values.length} test cases`);
127
+ ```
128
+
129
+ #### Create a Test Case
130
+
131
+ ```typescript
132
+ const testCase = await api.TestCase.createTestCase({
133
+ body: {
134
+ projectKey: 'PROJ',
135
+ name: 'Login Test Case',
136
+ objective: 'Verify user can login successfully',
137
+ precondition: 'User account exists',
138
+ priorityName: 'High',
139
+ statusName: 'Draft',
140
+ labels: ['automated', 'regression'],
141
+ customFields: {
142
+ 'Build Number': '2024.1',
143
+ 'Release Date': '2024-01-15'
144
+ }
145
+ }
146
+ });
147
+ console.log(`Created test case: ${testCase.key}`);
148
+ ```
149
+
150
+ #### Create a Test Cycle
151
+
152
+ ```typescript
153
+ const testCycle = await api.TestCycle.createTestCycle({
154
+ body: {
155
+ projectKey: 'PROJ',
156
+ name: 'Sprint 1 Regression',
157
+ description: 'Regression tests for Sprint 1',
158
+ plannedStartDate: '2024-01-15T09:00:00Z',
159
+ plannedEndDate: '2024-01-22T17:00:00Z',
160
+ statusName: 'In Progress',
161
+ customFields: {
162
+ 'Environment': 'Production'
163
+ }
164
+ }
165
+ });
166
+ console.log(`Created test cycle: ${testCycle.key}`);
167
+ ```
168
+
169
+ #### Create a Test Execution
170
+
171
+ ```typescript
172
+ const testExecution = await api.TestExecution.createTestExecution({
173
+ body: {
174
+ projectKey: 'PROJ',
175
+ testCaseKey: 'PROJ-T1',
176
+ testCycleKey: 'PROJ-R1',
177
+ statusName: 'Pass',
178
+ environmentName: 'Chrome Latest',
179
+ executionTime: 120000, // 2 minutes in milliseconds
180
+ comment: 'Test passed successfully'
181
+ }
182
+ });
183
+ console.log(`Created test execution: ${testExecution.key || testExecution.id}`);
184
+ ```
185
+
186
+ #### Update a Test Case
187
+
188
+ ```typescript
189
+ // First, get the test case
190
+ const testCase = await api.TestCase.getTestCase({ testCaseKey: 'PROJ-T1' });
191
+
192
+ // Update it
193
+ testCase.name = 'Updated Test Case Name';
194
+ testCase.objective = 'Updated objective';
195
+ testCase.labels = [...(testCase.labels || []), 'updated'];
196
+
197
+ await api.TestCase.updateTestCase({
198
+ testCaseKey: 'PROJ-T1',
199
+ body: testCase
200
+ });
201
+ ```
202
+
203
+ #### List with Filters and Pagination
204
+
205
+ ```typescript
206
+ // Offset-based pagination
207
+ const testCases = await api.TestCase.listTestCases({
208
+ projectKey: 'PROJ',
209
+ folderId: 12345,
210
+ maxResults: 50,
211
+ startAt: 0
212
+ });
213
+
214
+ // Cursor-based pagination (for large datasets)
215
+ const testCasesCursor = await api.TestCase.listTestCasesCursorPaginated({
216
+ projectKey: 'PROJ',
217
+ limit: 100,
218
+ startAtId: 0
219
+ });
220
+ ```
221
+
222
+ #### Create Links
223
+
224
+ ```typescript
225
+ // Link test case to Jira issue
226
+ const issueLink = await api.TestCase.createTestCaseIssueLink({
227
+ testCaseKey: 'PROJ-T1',
228
+ body: {
229
+ issueId: 12345
230
+ }
231
+ });
232
+
233
+ // Link test case to web URL
234
+ const webLink = await api.TestCase.createTestCaseWebLink({
235
+ testCaseKey: 'PROJ-T1',
236
+ body: {
237
+ url: 'https://example.com/test-resource',
238
+ description: 'External test resource'
239
+ }
240
+ });
241
+ ```
242
+
243
+ ## API Groups
244
+
245
+ The Managed API is organized into the following groups:
246
+
247
+ ### Project
248
+ - `listProjects(options?)` - List all projects
249
+ - `getProject(options)` - Get a specific project
250
+
251
+ ### Status
252
+ - `listStatuses(options)` - List all statuses
253
+ - `getStatus(options)` - Get a specific status
254
+ - `createStatus(request)` - Create a new status
255
+ - `updateStatus(request)` - Update a status
256
+
257
+ ### Priority
258
+ - `listPriorities(options)` - List all priorities
259
+ - `getPriority(options)` - Get a specific priority
260
+ - `createPriority(request)` - Create a new priority
261
+ - `updatePriority(request)` - Update a priority
262
+
263
+ ### Environment
264
+ - `listEnvironments(options)` - List all environments
265
+ - `getEnvironment(options)` - Get a specific environment
266
+ - `createEnvironment(request)` - Create a new environment
267
+ - `updateEnvironment(request)` - Update an environment
268
+
269
+ ### Folder
270
+ - `listFolders(options)` - List all folders
271
+ - `getFolder(options)` - Get a specific folder
272
+ - `createFolder(request)` - Create a new folder
273
+
274
+ ### TestCase
275
+ - `listTestCases(options?)` - List all test cases
276
+ - `listTestCasesCursorPaginated(options?)` - List test cases with cursor pagination
277
+ - `getTestCase(options)` - Get a specific test case
278
+ - `createTestCase(request)` - Create a new test case
279
+ - `updateTestCase(request)` - Update a test case
280
+ - `getTestCaseLinks(testCaseKey)` - Get links for a test case
281
+ - `createTestCaseIssueLink(request)` - Create issue link
282
+ - `createTestCaseWebLink(request)` - Create web link
283
+ - `listTestCaseVersions(options)` - List test case versions
284
+ - `getTestCaseVersion(options)` - Get a specific version
285
+ - `getTestCaseTestScript(testCaseKey)` - Get test script
286
+ - `createTestCaseTestScript(request)` - Create or update test script
287
+ - `getTestCaseTestSteps(testCaseKey, options?)` - Get test steps
288
+ - `createTestCaseTestSteps(request)` - Create test steps
289
+
290
+ ### TestCycle
291
+ - `listTestCycles(options?)` - List all test cycles
292
+ - `getTestCycle(options)` - Get a specific test cycle
293
+ - `createTestCycle(request)` - Create a new test cycle
294
+ - `updateTestCycle(request)` - Update a test cycle
295
+ - `getTestCycleLinks(testCycleIdOrKey)` - Get links for a test cycle
296
+ - `createTestCycleIssueLink(request)` - Create issue link
297
+ - `createTestCycleWebLink(request)` - Create web link
298
+
299
+ ### TestPlan
300
+ - `listTestPlans(options?)` - List all test plans
301
+ - `getTestPlan(options)` - Get a specific test plan
302
+ - `createTestPlan(request)` - Create a new test plan
303
+ - `createTestPlanIssueLink(request)` - Create issue link
304
+ - `createTestPlanWebLink(request)` - Create web link
305
+ - `createTestPlanTestCycleLink(request)` - Link test cycle to test plan
306
+
307
+ ### TestExecution
308
+ - `listTestExecutions(options?)` - List all test executions
309
+ - `listTestExecutionsNextgen(options?)` - List test executions with cursor pagination
310
+ - `getTestExecution(options)` - Get a specific test execution
311
+ - `createTestExecution(request)` - Create a new test execution
312
+ - `updateTestExecution(request)` - Update a test execution
313
+ - `getTestExecutionTestSteps(options)` - Get test steps for execution
314
+ - `putTestExecutionTestSteps(request)` - Update test steps
315
+ - `syncTestExecutionScript(request)` - Sync with test case script
316
+ - `listTestExecutionLinks(testExecutionIdOrKey)` - Get links
317
+ - `createTestExecutionIssueLink(request)` - Create issue link
318
+
319
+ ### Link
320
+ - `deleteLink(options)` - Delete a link (idempotent)
321
+
322
+ ### IssueLink
323
+ - `getIssueLinkTestCases(options)` - Get test cases linked to issue
324
+ - `getIssueLinkTestCycles(options)` - Get test cycles linked to issue
325
+ - `getIssueLinkTestPlans(options)` - Get test plans linked to issue
326
+ - `getIssueLinkExecutions(options)` - Get test executions linked to issue
327
+
328
+ ### Automation
329
+ - `createCustomExecutions(request)` - Upload custom format results
330
+ - `createCucumberExecutions(request)` - Upload Cucumber results
331
+ - `createJUnitExecutions(request)` - Upload JUnit XML results
332
+ - `retrieveBDDTestCases(options)` - Retrieve BDD feature files
333
+
334
+ ### Private API ⚠️
335
+ > **⚠️ WARNING: Private API Methods**
336
+ >
337
+ > The following methods use Zephyr's private/unofficial API endpoints that are:
338
+ > - **Not officially supported** by SmartBear
339
+ > - **Not part of the public API documentation**
340
+ > - **Subject to change at any time** without notice
341
+ > - **Not covered by Standard Support**
342
+ >
343
+ > Use these methods at your own risk. They may break with future Zephyr updates.
344
+
345
+ - `getContextJwt(userEmail, apiToken, jiraInstanceUrl)` - Get Jira Context JWT token (required for private API calls)
346
+ - `createCustomField(userEmail, apiToken, jiraInstanceUrl, category, request)` - Create custom fields for test cases, test plans, test runs, or test steps
347
+ - `createTestCaseVersion(userEmail, apiToken, jiraInstanceUrl, testCaseId, projectId)` - Create a new test case version
348
+
349
+ ## Authentication
350
+
351
+ ### Using ScriptRunner Connect API Connection (Recommended)
352
+
353
+ When using in ScriptRunner Connect, configure a Generic Connector with the full base URL in the web UI:
354
+
355
+ **For US region:** `https://api.zephyrscale.smartbear.com/v2`
356
+ **For EU region:** `https://eu.api.zephyrscale.smartbear.com/v2`
357
+
358
+ Then use it in your script:
359
+
360
+ ```typescript
361
+ import Zephyr from '@rbaileysr/zephyr-managed-api';
362
+ import ZephyrApiConnection from '../api/zephyr';
363
+
364
+ // Base URL is configured in the Generic Connector
365
+ const api = Zephyr.createZephyrApi(ZephyrApiConnection);
366
+ ```
367
+
368
+ ### Using OAuth Token
369
+
370
+ For local testing or when using OAuth tokens directly, provide the full base URL:
371
+
372
+ ```typescript
373
+ import Zephyr from '@rbaileysr/zephyr-managed-api';
374
+
375
+ // US region
376
+ const api = Zephyr.createZephyrApi(
377
+ 'your-oauth-token',
378
+ 'https://api.zephyrscale.smartbear.com/v2'
379
+ );
380
+
381
+ // EU region
382
+ const apiEU = Zephyr.createZephyrApi(
383
+ 'your-oauth-token',
384
+ 'https://eu.api.zephyrscale.smartbear.com/v2'
385
+ );
386
+ ```
387
+
388
+ ## Private API ⚠️
389
+
390
+ > **⚠️ WARNING: Private API Usage**
391
+ >
392
+ > The Private API methods use Zephyr's private/unofficial endpoints that are not officially supported by SmartBear. These endpoints may change or be removed at any time without notice and are not covered by Standard Support. Use these methods at your own risk.
393
+
394
+ The Private API group provides access to functionality not available in the public Zephyr API, such as creating custom fields and test case versions. These methods require Jira user credentials (email and API token) rather than OAuth tokens.
395
+
396
+ ### Authentication
397
+
398
+ Private API methods use Jira Basic Authentication (email + API token) to retrieve a Context JWT token, which is then used to authenticate with Zephyr's private endpoints.
399
+
400
+ ### Get Context JWT
401
+
402
+ The Context JWT is a short-lived token (15 minutes) required for all private API calls:
403
+
404
+ ```typescript
405
+ import Zephyr from '@rbaileysr/zephyr-managed-api';
406
+ import ZephyrApiConnection from '../api/zephyr';
407
+
408
+ const api = Zephyr.createZephyrApi(ZephyrApiConnection);
409
+
410
+ // Get Context JWT token
411
+ const contextJwt = await api.Private.getContextJwt(
412
+ 'user@example.com',
413
+ 'jira-api-token',
414
+ 'https://your-instance.atlassian.net'
415
+ );
416
+ ```
417
+
418
+ ### Create Custom Fields
419
+
420
+ Create custom fields for test cases, test plans, test runs, or test steps:
421
+
422
+ ```typescript
423
+ // Create a basic custom field for test cases
424
+ const customField = await api.Private.createCustomField(
425
+ 'user@example.com',
426
+ 'jira-api-token',
427
+ 'https://your-instance.atlassian.net',
428
+ 'TEST_CASE', // Category: TEST_CASE, TEST_PLAN, TEST_RUN, or TEST_STEP
429
+ {
430
+ projectId: 10017,
431
+ name: 'Build Number',
432
+ type: 'SINGLE_LINE_TEXT', // CHECKBOX, NUMBER, DECIMAL, SINGLE_LINE_TEXT, MULTI_LINE_TEXT, SINGLE_CHOICE_SELECT_LIST, MULTI_CHOICE_SELECT_LIST, USER_LIST, DATE
433
+ required: false,
434
+ index: 4,
435
+ category: 'TEST_CASE',
436
+ options: [], // Only for SINGLE_CHOICE_SELECT_LIST or MULTI_CHOICE_SELECT_LIST
437
+ archived: false
438
+ }
439
+ );
440
+
441
+ // Create a custom field with options (select list)
442
+ const selectField = await api.Private.createCustomField(
443
+ 'user@example.com',
444
+ 'jira-api-token',
445
+ 'https://your-instance.atlassian.net',
446
+ 'TEST_CASE',
447
+ {
448
+ projectId: 10017,
449
+ name: 'Test Category',
450
+ type: 'MULTI_CHOICE_SELECT_LIST',
451
+ required: false,
452
+ index: 5,
453
+ category: 'TEST_CASE',
454
+ options: [
455
+ { index: 1, name: 'Smoke', archived: false },
456
+ { index: 2, name: 'Regression', archived: false },
457
+ { index: 3, name: 'Performance', archived: false }
458
+ ],
459
+ archived: false
460
+ }
461
+ );
462
+ ```
463
+
464
+ **Available Custom Field Types:**
465
+ - `CHECKBOX` - Checkbox
466
+ - `NUMBER` - Number
467
+ - `DECIMAL` - Decimal number
468
+ - `SINGLE_LINE_TEXT` - Single-line text
469
+ - `MULTI_LINE_TEXT` - Multi-line text
470
+ - `SINGLE_CHOICE_SELECT_LIST` - Single-choice select list
471
+ - `MULTI_CHOICE_SELECT_LIST` - Multi-choice select list
472
+ - `USER_LIST` - User picker
473
+ - `DATE` - Date picker
474
+
475
+ **Available Categories:**
476
+ - `TEST_CASE` - Custom field for test cases
477
+ - `TEST_PLAN` - Custom field for test plans
478
+ - `TEST_RUN` - Custom field for test cycles (test runs)
479
+ - `TEST_STEP` - Custom field for test steps
480
+
481
+ ### Create Test Case Version
482
+
483
+ Create a new version of an existing test case. **Note:** When a new version is created, the `testCaseId` changes for that test case.
484
+
485
+ ```typescript
486
+ // First, get the test case to find its numeric ID
487
+ const testCase = await api.TestCase.getTestCase({ testCaseKey: 'PROJ-T1' });
488
+ const testCaseId = testCase.id; // Numeric ID, not the key
489
+
490
+ // Create a new version
491
+ const newVersion = await api.Private.createTestCaseVersion(
492
+ 'user@example.com',
493
+ 'jira-api-token',
494
+ 'https://your-instance.atlassian.net',
495
+ testCaseId, // Numeric test case ID
496
+ 10017 // Project ID or key
497
+ );
498
+ ```
499
+
500
+ **Important Notes:**
501
+ - The `testCaseId` must be the numeric ID, not the test case key (e.g., `PROJ-T1`)
502
+ - Use `api.TestCase.getTestCase()` to get the numeric ID from a test case key
503
+ - If a new version already exists, the API will return a 409 Conflict error
504
+ - The test case ID changes after creating a new version
505
+
506
+ ### Error Handling for Private API
507
+
508
+ Private API methods use the same error types as the public API:
509
+
510
+ ```typescript
511
+ try {
512
+ await api.Private.createCustomField(...);
513
+ } catch (error) {
514
+ if (error instanceof Zephyr.BadRequestError) {
515
+ console.log('Invalid request parameters');
516
+ } else if (error instanceof Zephyr.UnauthorizedError) {
517
+ console.log('Authentication failed - check your credentials');
518
+ } else if (error instanceof Zephyr.ForbiddenError) {
519
+ console.log('Insufficient permissions');
520
+ } else if (error instanceof Zephyr.NotFoundError) {
521
+ console.log('Resource not found');
522
+ } else if (error instanceof Zephyr.ServerError) {
523
+ if (error.status === 409) {
524
+ console.log('Conflict - version already exists');
525
+ }
526
+ }
527
+ }
528
+ ```
529
+
530
+ ## Custom Fields
531
+
532
+ Custom fields are supported as flexible key-value pairs:
533
+
534
+ ```typescript
535
+ const customFields = {
536
+ 'Build Number': 20,
537
+ 'Release Date': '2024-01-15',
538
+ 'Pre-Condition(s)': 'User should have logged in.<br>User should have navigated to the panel.',
539
+ 'Implemented': false,
540
+ 'Category': ['Performance', 'Regression'],
541
+ 'Tester': 'user-account-id-here'
542
+ };
543
+
544
+ await api.TestCase.createTestCase({
545
+ body: {
546
+ projectKey: 'PROJ',
547
+ name: 'Test Case',
548
+ customFields: customFields
549
+ }
550
+ });
551
+ ```
552
+
553
+ **Note**:
554
+ - Multi-line text fields support HTML with `<br>` tags
555
+ - Dates should be in format `yyyy-MM-dd`
556
+ - Users should have values of Jira User Account IDs
557
+ - Custom field types are defined in your Zephyr project configuration
558
+
559
+ ## Error Handling
560
+
561
+ The API uses Commons Core-compatible error types for consistent error handling. All API methods automatically retry on rate limiting (HTTP 429) with exponential backoff.
562
+
563
+ ### Error Types
564
+
565
+ ```typescript
566
+ // Single import for everything
567
+ import Zephyr from '@rbaileysr/zephyr-managed-api';
568
+ import ZephyrApiConnection from '../api/zephyr';
569
+
570
+ const api = Zephyr.createZephyrApi(ZephyrApiConnection);
571
+
572
+ try {
573
+ const testCase = await api.TestCase.getTestCase({ testCaseKey: 'INVALID-T1' });
574
+ } catch (error) {
575
+ if (error instanceof Zephyr.NotFoundError) {
576
+ console.log('Test case not found');
577
+ } else if (error instanceof Zephyr.BadRequestError) {
578
+ console.log('Invalid request');
579
+ } else if (error instanceof Zephyr.TooManyRequestsError) {
580
+ console.log('Rate limited - this should have been retried automatically');
581
+ } else if (error instanceof Zephyr.HttpError) {
582
+ console.log(`HTTP error: ${error.message}`);
583
+ }
584
+ }
585
+ ```
586
+
587
+ **Alternative - Named Imports:**
588
+ ```typescript
589
+ import {
590
+ createZephyrApi,
591
+ BadRequestError,
592
+ UnauthorizedError,
593
+ ForbiddenError,
594
+ NotFoundError,
595
+ TooManyRequestsError,
596
+ ServerError,
597
+ HttpError,
598
+ UnexpectedError
599
+ } from '@rbaileysr/zephyr-managed-api';
600
+
601
+ // Use named imports as before
602
+ ```
603
+
604
+ ### Automatic Rate Limiting Handling
605
+
606
+ All API methods automatically handle rate limiting (HTTP 429) with:
607
+
608
+ - **Exponential Backoff**: Starts at 1 second, doubles with each retry
609
+ - **Retry-After Header Support**: Respects `Retry-After` header when present
610
+ - **Configurable Retries**: Defaults to 5 retries, max delay of 60 seconds
611
+ - **Transparent Operation**: Retries happen automatically - no code changes needed
612
+
613
+ **Default Retry Configuration:**
614
+ - Maximum retries: 5
615
+ - Initial delay: 1 second
616
+ - Maximum delay: 60 seconds
617
+ - Backoff multiplier: 2x
618
+
619
+ **Example:**
620
+ ```typescript
621
+ // This call will automatically retry on 429 errors
622
+ // You don't need to handle rate limiting manually
623
+ const testCases = await api.TestCase.listTestCases({ projectKey: 'PROJ' });
624
+ ```
625
+
626
+ If rate limiting occurs:
627
+ 1. The API waits (using Retry-After header if present, otherwise exponential backoff)
628
+ 2. Retries the request automatically
629
+ 3. Repeats up to 5 times
630
+ 4. Only throws `TooManyRequestsError` if all retries are exhausted
631
+
632
+ ### Error Strategy Support
633
+
634
+ For advanced error handling, you can provide an `errorStrategy` parameter to customize how errors are handled:
635
+
636
+ ```typescript
637
+ import Zephyr from '@rbaileysr/zephyr-managed-api';
638
+ import ZephyrApiConnection from '../api/zephyr';
639
+
640
+ const api = Zephyr.createZephyrApi(ZephyrApiConnection);
641
+
642
+ // Return null instead of throwing on 404
643
+ const testCase = await api.TestCase.getTestCase(
644
+ { testCaseKey: 'PROJ-T1' },
645
+ {
646
+ handleHttp404Error: () => ({ type: 'return', value: null })
647
+ }
648
+ );
649
+
650
+ // Using ErrorStrategyBuilder for cleaner syntax
651
+ const errorStrategy = new Zephyr.ErrorStrategyBuilder<TestCase | null>()
652
+ .http404Error(() => ({ type: 'return', value: null }))
653
+ .retryOnRateLimiting(10) // Retry up to 10 times on rate limiting
654
+ .http500Error(() => Zephyr.retry(5000)) // Retry server errors with 5s delay
655
+ .build();
656
+
657
+ const testCase2 = await api.TestCase.getTestCase(
658
+ { testCaseKey: 'PROJ-T2' },
659
+ errorStrategy
660
+ );
661
+ ```
662
+
663
+ **Available Error Strategy Handlers:**
664
+ - `handleHttp400Error` - Handle 400 Bad Request
665
+ - `handleHttp401Error` - Handle 401 Unauthorized
666
+ - `handleHttp403Error` - Handle 403 Forbidden
667
+ - `handleHttp404Error` - Handle 404 Not Found
668
+ - `handleHttp429Error` - Handle 429 Too Many Requests (receives retryCount)
669
+ - `handleHttp500Error` - Handle 500+ Server Errors
670
+ - `handleHttpError` - Handle any HTTP error (fallback)
671
+
672
+ **Error Strategy Actions:**
673
+ - `retry(delay)` - Retry the request after the specified delay (milliseconds)
674
+ - `continuePropagation()` - Let the error propagate normally (default)
675
+ - `{ type: 'return', value: T }` - Return a specific value instead of throwing
676
+
677
+ ### Error Type Reference
678
+
679
+ | Error Type | HTTP Status | Description |
680
+ |------------|-------------|-------------|
681
+ | `BadRequestError` | 400 | Invalid request parameters |
682
+ | `UnauthorizedError` | 401 | Authentication required or invalid |
683
+ | `ForbiddenError` | 403 | Insufficient permissions |
684
+ | `NotFoundError` | 404 | Resource not found |
685
+ | `TooManyRequestsError` | 429 | Rate limit exceeded (after retries) |
686
+ | `ServerError` | 500+ | Server-side error |
687
+ | `HttpError` | Other | Generic HTTP error |
688
+ | `UnexpectedError` | N/A | Network or parsing errors |
689
+
690
+ ## Pagination
691
+
692
+ ### Manual Pagination
693
+
694
+ #### Offset-Based Pagination
695
+
696
+ ```typescript
697
+ const testCases = await api.TestCase.listTestCases({
698
+ projectKey: 'PROJ',
699
+ maxResults: 50,
700
+ startAt: 0
701
+ });
702
+
703
+ // Check if more results available
704
+ if (!testCases.isLast) {
705
+ const nextPage = await api.TestCase.listTestCases({
706
+ projectKey: 'PROJ',
707
+ maxResults: 50,
708
+ startAt: testCases.startAt + testCases.maxResults
709
+ });
710
+ }
711
+ ```
712
+
713
+ #### Cursor-Based Pagination
714
+
715
+ ```typescript
716
+ const testCases = await api.TestCase.listTestCasesCursorPaginated({
717
+ projectKey: 'PROJ',
718
+ limit: 100,
719
+ startAtId: 0
720
+ });
721
+
722
+ // Get next page
723
+ if (testCases.nextStartAtId !== null) {
724
+ const nextPage = await api.TestCase.listTestCasesCursorPaginated({
725
+ projectKey: 'PROJ',
726
+ limit: 100,
727
+ startAtId: testCases.nextStartAtId
728
+ });
729
+ }
730
+ ```
731
+
732
+ ### Automatic Pagination with `getAllPages()`
733
+
734
+ For convenience, use `getAllPages()` to automatically fetch all pages:
735
+
736
+ #### Offset-Based Pagination
737
+
738
+ ```typescript
739
+ import Zephyr from '@rbaileysr/zephyr-managed-api';
740
+ import ZephyrApiConnection from '../api/zephyr';
741
+
742
+ const api = Zephyr.createZephyrApi(ZephyrApiConnection);
743
+
744
+ // Automatically fetches all pages
745
+ const allTestCases = await Zephyr.getAllPages(
746
+ (startAt, maxResults) => api.TestCase.listTestCases({
747
+ projectKey: 'PROJ',
748
+ startAt,
749
+ maxResults
750
+ }),
751
+ { maxResults: 50 } // Optional: page size
752
+ );
753
+
754
+ // allTestCases is an array of all TestCase objects from all pages
755
+ console.log(`Found ${allTestCases.length} test cases`);
756
+ ```
757
+
758
+ #### Cursor-Based Pagination
759
+
760
+ ```typescript
761
+ import Zephyr from '@rbaileysr/zephyr-managed-api';
762
+ import ZephyrApiConnection from '../api/zephyr';
763
+
764
+ const api = Zephyr.createZephyrApi(ZephyrApiConnection);
765
+
766
+ // Automatically fetches all pages
767
+ const allTestCases = await Zephyr.getAllPagesCursor(
768
+ (startAtId, limit) => api.TestCase.listTestCasesCursorPaginated({
769
+ projectKey: 'PROJ',
770
+ startAtId,
771
+ limit
772
+ }),
773
+ { limit: 100 } // Optional: page size
774
+ );
775
+
776
+ // allTestCases is an array of all TestCase objects from all pages
777
+ console.log(`Found ${allTestCases.length} test cases`);
778
+ ```
779
+
780
+ **Note**: `getAllPages()` and `getAllPagesCursor()` automatically handle pagination and will make multiple API calls as needed. Be mindful of rate limits when fetching large datasets.
781
+
782
+ ## API Documentation
783
+
784
+ All API methods include comprehensive JSDoc comments with:
785
+ - Detailed descriptions of what each endpoint does
786
+ - Parameter documentation with types and requirements
787
+ - Return type documentation
788
+ - Links to official Zephyr API documentation
789
+
790
+ When using the package in ScriptRunner Connect, IntelliSense will display these descriptions and links in the code editor.
791
+
792
+ ## Type Definitions
793
+
794
+ All TypeScript types are exported from the main module:
795
+
796
+ ```typescript
797
+ import type {
798
+ TestCase,
799
+ TestCycle,
800
+ TestExecution,
801
+ TestPlan,
802
+ CustomFields,
803
+ ZephyrApiConnection,
804
+ ErrorStrategy,
805
+ ErrorStrategyBuilder
806
+ } from '@rbaileysr/zephyr-managed-api';
807
+ ```
808
+
809
+ ## All Group
810
+
811
+ For programmatic access to all methods, use the `All` group:
812
+
813
+ ```typescript
814
+ // Access all methods through the All group
815
+ const testCase = await Zephyr.All.getTestCase({ testCaseKey: 'PROJ-T1' });
816
+ const testCycle = await Zephyr.All.createTestCycle({ body: { ... } });
817
+ ```
818
+
819
+ The `All` group provides a single point of access to all API methods across all resource groups, useful for dynamic method invocation or when you need to iterate over all available methods.
820
+
821
+ ## License
822
+
823
+ UNLICENSED - Copyright Adaptavist 2025 (c) All rights reserved
824
+
825
+ ## Support
826
+
827
+ For issues, questions, or contributions, please refer to the project repository on GitHub.
828
+
829
+ ## Links
830
+
831
+ - **NPM Package**: https://www.npmjs.com/package/@rbaileysr/zephyr-managed-api
832
+ - **Zephyr API Documentation**: https://support.smartbear.com/zephyr-scale-cloud/api-docs/v2/
833
+ - **ScriptRunner Connect Documentation**: https://docs.adaptavist.com/src/latest/
834
+
835
+ ## Changelog
836
+
837
+ ### 1.1.0
838
+
839
+ - **Added**: Private API group with support for unofficial Zephyr endpoints
840
+ - `getContextJwt()` - Retrieve Jira Context JWT token for private API authentication
841
+ - `createCustomField()` - Create custom fields for test cases, test plans, test runs, and test steps
842
+ - `createTestCaseVersion()` - Create new test case versions
843
+ - **Added**: Type definitions for private API requests (`CreatePrivateCustomFieldRequest`, `PrivateCustomFieldType`, `PrivateCustomFieldCategory`, etc.)
844
+ - **Warning**: Private API methods are not officially supported and may change without notice
845
+
846
+ ### 1.0.1
847
+
848
+ - **Added**: Comprehensive JSDoc comments with API endpoint descriptions and links to official documentation
849
+ - **Added**: Single import option via default export for convenient usage
850
+ - **Changed**: Removed `region` parameter from `createZephyrApi` - base URL now configured in Generic Connector
851
+ - **Improved**: Error handling to support both `BadRequestError` and `NotFoundError` in test examples
852
+ - **Improved**: Type safety and IntelliSense support throughout the API
853
+
854
+ ### 1.0.0
855
+
856
+ - **Initial Release**: Complete Managed API wrapper for Zephyr Cloud REST API v2
857
+ - **Added**: Full support for all Zephyr API endpoints (Test Cases, Test Cycles, Test Plans, Test Executions, etc.)
858
+ - **Added**: Type-safe TypeScript definitions for all API operations
859
+ - **Added**: Hierarchical API structure matching ScriptRunner Connect conventions
860
+ - **Added**: Automatic rate limiting retry with exponential backoff
861
+ - **Added**: Support for both API Connection and OAuth token authentication
862
+ - **Added**: Pagination helpers (`getAllPages`, `getAllPagesCursor`)
863
+ - **Added**: Error strategy builder for advanced error handling
864
+ - **Added**: Comprehensive error types compatible with Commons Core
865
+