@dichovsky/testrail-api-client 1.0.0 → 2.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 (137) hide show
  1. package/README.md +22 -0
  2. package/dist/cli/auth.d.ts +21 -0
  3. package/dist/cli/auth.js +16 -0
  4. package/dist/cli/body.d.ts +42 -0
  5. package/dist/cli/body.js +89 -0
  6. package/dist/cli/dispatch.d.ts +16 -0
  7. package/dist/cli/dispatch.js +87 -0
  8. package/dist/cli/handler-context.d.ts +43 -0
  9. package/dist/cli/handler-context.js +2 -0
  10. package/dist/cli/handlers/case-write.d.ts +4 -0
  11. package/dist/cli/handlers/case-write.js +26 -0
  12. package/dist/cli/handlers/case.d.ts +4 -0
  13. package/dist/cli/handlers/case.js +11 -0
  14. package/dist/cli/handlers/milestone.d.ts +4 -0
  15. package/dist/cli/handlers/milestone.js +15 -0
  16. package/dist/cli/handlers/project.d.ts +4 -0
  17. package/dist/cli/handlers/project.js +11 -0
  18. package/dist/cli/handlers/result-write.d.ts +4 -0
  19. package/dist/cli/handlers/result-write.js +40 -0
  20. package/dist/cli/handlers/result.d.ts +3 -0
  21. package/dist/cli/handlers/result.js +11 -0
  22. package/dist/cli/handlers/run-write.d.ts +10 -0
  23. package/dist/cli/handlers/run-write.js +29 -0
  24. package/dist/cli/handlers/run.d.ts +4 -0
  25. package/dist/cli/handlers/run.js +15 -0
  26. package/dist/cli/handlers/suite.d.ts +4 -0
  27. package/dist/cli/handlers/suite.js +10 -0
  28. package/dist/cli/handlers/user.d.ts +4 -0
  29. package/dist/cli/handlers/user.js +11 -0
  30. package/dist/cli/ids.d.ts +6 -0
  31. package/dist/cli/ids.js +20 -0
  32. package/dist/cli/index.d.ts +3 -0
  33. package/dist/cli/index.js +198 -0
  34. package/dist/cli/install-skill.d.ts +35 -0
  35. package/dist/cli/install-skill.js +71 -0
  36. package/dist/cli/metadata.d.ts +37 -0
  37. package/dist/cli/metadata.js +151 -0
  38. package/dist/cli/output.d.ts +28 -0
  39. package/dist/cli/output.js +84 -0
  40. package/dist/cli.d.ts +1 -1
  41. package/dist/cli.js +1 -266
  42. package/dist/client-core.d.ts +16 -7
  43. package/dist/client-core.js +153 -27
  44. package/dist/client.d.ts +274 -118
  45. package/dist/client.js +404 -463
  46. package/dist/constants.d.ts +1 -0
  47. package/dist/constants.js +1 -0
  48. package/dist/errors.d.ts +11 -9
  49. package/dist/errors.js +12 -8
  50. package/dist/index.d.ts +4 -2
  51. package/dist/index.js +2 -1
  52. package/dist/modules/attachments.d.ts +19 -0
  53. package/dist/modules/attachments.js +64 -0
  54. package/dist/modules/cases.d.ts +13 -0
  55. package/dist/modules/cases.js +58 -0
  56. package/dist/modules/configurations.d.ts +14 -0
  57. package/dist/modules/configurations.js +37 -0
  58. package/dist/modules/datasets.d.ts +12 -0
  59. package/dist/modules/datasets.js +28 -0
  60. package/dist/modules/metadata.d.ts +14 -0
  61. package/dist/modules/metadata.js +31 -0
  62. package/dist/modules/milestones.d.ts +12 -0
  63. package/dist/modules/milestones.js +36 -0
  64. package/dist/modules/plans.d.ts +16 -0
  65. package/dist/modules/plans.js +59 -0
  66. package/dist/modules/projects.d.ts +36 -0
  67. package/dist/modules/projects.js +55 -0
  68. package/dist/modules/reports.d.ts +9 -0
  69. package/dist/modules/reports.js +16 -0
  70. package/dist/modules/results.d.ts +14 -0
  71. package/dist/modules/results.js +69 -0
  72. package/dist/modules/runs.d.ts +14 -0
  73. package/dist/modules/runs.js +57 -0
  74. package/dist/modules/sections.d.ts +16 -0
  75. package/dist/modules/sections.js +37 -0
  76. package/dist/modules/sharedSteps.d.ts +12 -0
  77. package/dist/modules/sharedSteps.js +28 -0
  78. package/dist/modules/suites.d.ts +37 -0
  79. package/dist/modules/suites.js +54 -0
  80. package/dist/modules/tests.d.ts +9 -0
  81. package/dist/modules/tests.js +25 -0
  82. package/dist/modules/users.d.ts +18 -0
  83. package/dist/modules/users.js +62 -0
  84. package/dist/modules/variables.d.ts +11 -0
  85. package/dist/modules/variables.js +24 -0
  86. package/dist/schemas.d.ts +544 -0
  87. package/dist/schemas.js +419 -0
  88. package/dist/types.d.ts +1 -55
  89. package/dist/utils.d.ts +2 -0
  90. package/dist/utils.js +4 -0
  91. package/package.json +23 -15
  92. package/skill/SKILL.md +395 -0
  93. package/src/cli/auth.ts +37 -0
  94. package/src/cli/body.ts +100 -0
  95. package/src/cli/dispatch.ts +91 -0
  96. package/src/cli/handler-context.ts +46 -0
  97. package/src/cli/handlers/case-write.ts +26 -0
  98. package/src/cli/handlers/case.ts +13 -0
  99. package/src/cli/handlers/milestone.ts +19 -0
  100. package/src/cli/handlers/project.ts +13 -0
  101. package/src/cli/handlers/result-write.ts +40 -0
  102. package/src/cli/handlers/result.ts +14 -0
  103. package/src/cli/handlers/run-write.ts +30 -0
  104. package/src/cli/handlers/run.ts +19 -0
  105. package/src/cli/handlers/suite.ts +12 -0
  106. package/src/cli/handlers/user.ts +13 -0
  107. package/src/cli/ids.ts +20 -0
  108. package/src/cli/index.ts +224 -0
  109. package/src/cli/install-skill.ts +89 -0
  110. package/src/cli/metadata.ts +194 -0
  111. package/src/cli/output.ts +96 -0
  112. package/src/cli.ts +1 -286
  113. package/src/client-core.ts +183 -67
  114. package/src/client.ts +414 -483
  115. package/src/constants.ts +1 -0
  116. package/src/errors.ts +18 -11
  117. package/src/index.ts +50 -8
  118. package/src/modules/attachments.ts +125 -0
  119. package/src/modules/cases.ts +78 -0
  120. package/src/modules/configurations.ts +68 -0
  121. package/src/modules/datasets.ts +44 -0
  122. package/src/modules/metadata.ts +63 -0
  123. package/src/modules/milestones.ts +54 -0
  124. package/src/modules/plans.ts +89 -0
  125. package/src/modules/projects.ts +67 -0
  126. package/src/modules/reports.ts +23 -0
  127. package/src/modules/results.ts +90 -0
  128. package/src/modules/runs.ts +70 -0
  129. package/src/modules/sections.ts +55 -0
  130. package/src/modules/sharedSteps.ts +44 -0
  131. package/src/modules/suites.ts +67 -0
  132. package/src/modules/tests.ts +28 -0
  133. package/src/modules/users.ts +87 -0
  134. package/src/modules/variables.ts +36 -0
  135. package/src/schemas.ts +551 -0
  136. package/src/types.ts +11 -60
  137. package/src/utils.ts +5 -0
package/src/constants.ts CHANGED
@@ -13,3 +13,4 @@ export const DEFAULT_CACHE_CLEANUP_INTERVAL_MS = 60000; // 1 minute
13
13
  export const DEFAULT_MAX_CACHE_SIZE = 1000;
14
14
  export const DEFAULT_RATE_LIMIT_MAX_REQUESTS = 100;
15
15
  export const DEFAULT_RATE_LIMIT_WINDOW_MS = 60000; // 1 minute
16
+ export const DEFAULT_DNS_VALIDATION_MAX_WAIT_MS = 2000;
package/src/errors.ts CHANGED
@@ -1,18 +1,15 @@
1
+ import { ZodError } from 'zod';
2
+
1
3
  /**
2
4
  * Thrown when the TestRail API returns a non-2xx response or a network error occurs.
3
- *
4
- * @property status - HTTP status code (if available)
5
- * @property statusText - HTTP status text (if available)
6
- * @property response - Raw response body (if available)
7
5
  */
8
6
  export class TestRailApiError extends Error {
9
7
  constructor(
10
- message: string,
11
- public readonly status?: number,
12
- public readonly statusText?: string,
13
- public readonly response?: string,
8
+ public readonly status: number,
9
+ public readonly statusText: string,
10
+ public readonly response?: unknown,
14
11
  ) {
15
- super(message);
12
+ super(`TestRail API error: ${status} ${statusText}`);
16
13
  this.name = 'TestRailApiError';
17
14
  }
18
15
  }
@@ -21,8 +18,18 @@ export class TestRailApiError extends Error {
21
18
  * Thrown when client configuration or method parameters fail validation.
22
19
  */
23
20
  export class TestRailValidationError extends Error {
24
- constructor(message: string) {
25
- super(message);
21
+ constructor(
22
+ message: string,
23
+ public readonly details?: unknown,
24
+ ) {
25
+ super(`TestRail Validation Error: ${message}`);
26
26
  this.name = 'TestRailValidationError';
27
27
  }
28
28
  }
29
+
30
+ /**
31
+ * Utility to convert ZodError into TestRailValidationError.
32
+ */
33
+ export function handleZodError(error: ZodError): TestRailValidationError {
34
+ return new TestRailValidationError('Schema validation failed', error.format());
35
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,54 @@
1
1
  export { TestRailClient } from './client.js';
2
- export { TestRailApiError, TestRailValidationError } from './errors.js';
2
+ export { TestRailApiError, TestRailValidationError, handleZodError } from './errors.js';
3
+ export {
4
+ TestRailConfigSchema,
5
+ PaginationSchema,
6
+ UserSchema,
7
+ RoleSchema,
8
+ GroupSchema,
9
+ ProjectSchema,
10
+ SuiteSchema,
11
+ CaseSchema,
12
+ SectionSchema,
13
+ RunSchema,
14
+ PlanEntrySchema,
15
+ PlanSchema,
16
+ TestSchema,
17
+ ResultSchema,
18
+ MilestoneSchema,
19
+ StatusSchema,
20
+ PrioritySchema,
21
+ CaseFieldConfigSchema,
22
+ CaseFieldSchema,
23
+ ResultFieldConfigSchema,
24
+ ResultFieldSchema,
25
+ CaseTypeSchema,
26
+ TemplateSchema,
27
+ ConfigurationSchema,
28
+ ConfigurationGroupSchema,
29
+ AttachmentSchema,
30
+ SharedStepSchema,
31
+ VariableSchema,
32
+ DatasetSchema,
33
+ ReportSchema,
34
+ ReportResultSchema,
35
+ AddCasePayloadSchema,
36
+ UpdateCasePayloadSchema,
37
+ AddRunPayloadSchema,
38
+ UpdateRunPayloadSchema,
39
+ AddResultPayloadSchema,
40
+ AddResultForCasePayloadSchema,
41
+ AddResultsForCasesPayloadSchema,
42
+ } from './schemas.js';
43
+ export type {
44
+ AddCasePayload,
45
+ UpdateCasePayload,
46
+ AddRunPayload,
47
+ UpdateRunPayload,
48
+ AddResultPayload,
49
+ AddResultForCasePayload,
50
+ AddResultsForCasesPayload,
51
+ } from './schemas.js';
3
52
  export type {
4
53
  TestRailConfig,
5
54
  RateLimiterConfig,
@@ -18,17 +67,10 @@ export type {
18
67
  User,
19
68
  Status,
20
69
  Priority,
21
- AddCasePayload,
22
- UpdateCasePayload,
23
70
  AddPlanPayload,
24
71
  UpdatePlanPayload,
25
72
  AddPlanEntryPayload,
26
73
  UpdatePlanEntryPayload,
27
- AddRunPayload,
28
- UpdateRunPayload,
29
- AddResultPayload,
30
- AddResultsForCasesPayload,
31
- AddResultForCasePayload,
32
74
  AddSectionPayload,
33
75
  UpdateSectionPayload,
34
76
  AddMilestonePayload,
@@ -0,0 +1,125 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Attachment } from '../types.js';
3
+ import { z } from 'zod';
4
+ import { AttachmentSchema } from '../schemas.js';
5
+
6
+ export class AttachmentModule {
7
+ constructor(private readonly client: TestRailClientCore) {}
8
+
9
+ async getAttachmentsForCase(caseId: number): Promise<Attachment[]> {
10
+ this.client.validateId(caseId, 'caseId');
11
+ const raw = await this.client.request<unknown>('GET', `get_attachments_for_case/${caseId}`);
12
+ return (
13
+ this.client.parse<{ attachments?: Attachment[] }>(
14
+ z.object({ attachments: z.array(AttachmentSchema).optional() }),
15
+ raw,
16
+ ).attachments ?? []
17
+ );
18
+ }
19
+
20
+ async getAttachmentsForRun(runId: number): Promise<Attachment[]> {
21
+ this.client.validateId(runId, 'runId');
22
+ const raw = await this.client.request<unknown>('GET', `get_attachments_for_run/${runId}`);
23
+ return (
24
+ this.client.parse<{ attachments?: Attachment[] }>(
25
+ z.object({ attachments: z.array(AttachmentSchema).optional() }),
26
+ raw,
27
+ ).attachments ?? []
28
+ );
29
+ }
30
+
31
+ async getAttachmentsForTest(testId: number): Promise<Attachment[]> {
32
+ this.client.validateId(testId, 'testId');
33
+ const raw = await this.client.request<unknown>('GET', `get_attachments_for_test/${testId}`);
34
+ return (
35
+ this.client.parse<{ attachments?: Attachment[] }>(
36
+ z.object({ attachments: z.array(AttachmentSchema).optional() }),
37
+ raw,
38
+ ).attachments ?? []
39
+ );
40
+ }
41
+
42
+ async getAttachmentsForPlan(planId: number): Promise<Attachment[]> {
43
+ this.client.validateId(planId, 'planId');
44
+ const raw = await this.client.request<unknown>('GET', `get_attachments_for_plan/${planId}`);
45
+ return (
46
+ this.client.parse<{ attachments?: Attachment[] }>(
47
+ z.object({ attachments: z.array(AttachmentSchema).optional() }),
48
+ raw,
49
+ ).attachments ?? []
50
+ );
51
+ }
52
+
53
+ async getAttachmentsForPlanEntry(planId: number, entryId: number): Promise<Attachment[]> {
54
+ this.client.validateId(planId, 'planId');
55
+ this.client.validateId(entryId, 'entryId');
56
+ const raw = await this.client.request<unknown>('GET', `get_attachments_for_plan_entry/${planId}/${entryId}`);
57
+ return (
58
+ this.client.parse<{ attachments?: Attachment[] }>(
59
+ z.object({ attachments: z.array(AttachmentSchema).optional() }),
60
+ raw,
61
+ ).attachments ?? []
62
+ );
63
+ }
64
+
65
+ async getAttachment(attachmentId: number): Promise<ArrayBuffer> {
66
+ this.client.validateId(attachmentId, 'attachmentId');
67
+ return this.client.requestBinary(`get_attachment/${attachmentId}`);
68
+ }
69
+
70
+ async addAttachmentToCase(
71
+ caseId: number,
72
+ file: globalThis.Blob | Uint8Array | globalThis.File,
73
+ filename: string,
74
+ ): Promise<Attachment> {
75
+ this.client.validateId(caseId, 'caseId');
76
+ return this.client.requestMultipart<Attachment>(`add_attachment_to_case/${caseId}`, file, filename);
77
+ }
78
+
79
+ async addAttachmentToResult(
80
+ resultId: number,
81
+ file: globalThis.Blob | Uint8Array | globalThis.File,
82
+ filename: string,
83
+ ): Promise<Attachment> {
84
+ this.client.validateId(resultId, 'resultId');
85
+ return this.client.requestMultipart<Attachment>(`add_attachment_to_result/${resultId}`, file, filename);
86
+ }
87
+
88
+ async addAttachmentToRun(
89
+ runId: number,
90
+ file: globalThis.Blob | Uint8Array | globalThis.File,
91
+ filename: string,
92
+ ): Promise<Attachment> {
93
+ this.client.validateId(runId, 'runId');
94
+ return this.client.requestMultipart<Attachment>(`add_attachment_to_run/${runId}`, file, filename);
95
+ }
96
+
97
+ async addAttachmentToPlan(
98
+ planId: number,
99
+ file: globalThis.Blob | Uint8Array | globalThis.File,
100
+ filename: string,
101
+ ): Promise<Attachment> {
102
+ this.client.validateId(planId, 'planId');
103
+ return this.client.requestMultipart<Attachment>(`add_attachment_to_plan/${planId}`, file, filename);
104
+ }
105
+
106
+ async addAttachmentToPlanEntry(
107
+ planId: number,
108
+ entryId: number,
109
+ file: globalThis.Blob | Uint8Array | globalThis.File,
110
+ filename: string,
111
+ ): Promise<Attachment> {
112
+ this.client.validateId(planId, 'planId');
113
+ this.client.validateId(entryId, 'entryId');
114
+ return this.client.requestMultipart<Attachment>(
115
+ `add_attachment_to_plan_entry/${planId}/${entryId}`,
116
+ file,
117
+ filename,
118
+ );
119
+ }
120
+
121
+ async deleteAttachment(attachmentId: number): Promise<void> {
122
+ this.client.validateId(attachmentId, 'attachmentId');
123
+ await this.client.request<void>('POST', `delete_attachment/${attachmentId}`);
124
+ }
125
+ }
@@ -0,0 +1,78 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Case, GetCasesOptions } from '../types.js';
3
+ import type { AddCasePayload, UpdateCasePayload } from '../schemas.js';
4
+ import { CaseSchema } from '../schemas.js';
5
+ import { z } from 'zod';
6
+
7
+ export class CaseModule {
8
+ constructor(private readonly client: TestRailClientCore) {}
9
+
10
+ async getCase(caseId: number): Promise<Case> {
11
+ this.client.validateId(caseId, 'caseId');
12
+ return this.client.parse<Case>(CaseSchema, await this.client.request<unknown>('GET', `get_case/${caseId}`));
13
+ }
14
+
15
+ async getCases(projectId: number, options?: GetCasesOptions): Promise<Case[]> {
16
+ this.client.validateId(projectId, 'projectId');
17
+ const {
18
+ suiteId,
19
+ sectionId,
20
+ typeId,
21
+ priorityId,
22
+ templateId,
23
+ milestoneId,
24
+ createdAfter,
25
+ createdBefore,
26
+ updatedAfter,
27
+ updatedBefore,
28
+ limit,
29
+ offset,
30
+ } = options ?? {};
31
+ if (suiteId !== undefined) this.client.validateId(suiteId, 'suiteId');
32
+ if (sectionId !== undefined) this.client.validateId(sectionId, 'sectionId');
33
+ if (typeId !== undefined) this.client.validateId(typeId, 'typeId');
34
+ if (priorityId !== undefined) this.client.validateId(priorityId, 'priorityId');
35
+ if (templateId !== undefined) this.client.validateId(templateId, 'templateId');
36
+ if (milestoneId !== undefined) this.client.validateId(milestoneId, 'milestoneId');
37
+ this.client.validatePaginationParams(limit, offset);
38
+ const endpoint = this.client.buildEndpoint(`get_cases/${projectId}`, {
39
+ suite_id: suiteId,
40
+ section_id: sectionId,
41
+ type_id: typeId,
42
+ priority_id: priorityId,
43
+ template_id: templateId,
44
+ milestone_id: milestoneId,
45
+ created_after: createdAfter,
46
+ created_before: createdBefore,
47
+ updated_after: updatedAfter,
48
+ updated_before: updatedBefore,
49
+ limit,
50
+ offset,
51
+ });
52
+ const raw = await this.client.request<unknown>('GET', endpoint);
53
+ return (
54
+ this.client.parse<{ cases?: Case[] }>(z.object({ cases: z.array(CaseSchema).optional() }), raw).cases ?? []
55
+ );
56
+ }
57
+
58
+ async addCase(sectionId: number, payload: AddCasePayload): Promise<Case> {
59
+ this.client.validateId(sectionId, 'sectionId');
60
+ return this.client.parse<Case>(
61
+ CaseSchema,
62
+ await this.client.request<unknown>('POST', `add_case/${sectionId}`, payload),
63
+ );
64
+ }
65
+
66
+ async updateCase(caseId: number, payload: UpdateCasePayload): Promise<Case> {
67
+ this.client.validateId(caseId, 'caseId');
68
+ return this.client.parse<Case>(
69
+ CaseSchema,
70
+ await this.client.request<unknown>('POST', `update_case/${caseId}`, payload),
71
+ );
72
+ }
73
+
74
+ async deleteCase(caseId: number): Promise<void> {
75
+ this.client.validateId(caseId, 'caseId');
76
+ await this.client.request<void>('POST', `delete_case/${caseId}`);
77
+ }
78
+ }
@@ -0,0 +1,68 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type {
3
+ ConfigurationGroup,
4
+ Configuration,
5
+ AddConfigurationGroupPayload,
6
+ UpdateConfigurationGroupPayload,
7
+ AddConfigurationPayload,
8
+ UpdateConfigurationPayload,
9
+ } from '../types.js';
10
+ import { ConfigurationGroupSchema, ConfigurationSchema } from '../schemas.js';
11
+ import { z } from 'zod';
12
+
13
+ export class ConfigurationModule {
14
+ constructor(private readonly client: TestRailClientCore) {}
15
+
16
+ async getConfigurations(projectId: number): Promise<ConfigurationGroup[]> {
17
+ this.client.validateId(projectId, 'projectId');
18
+ return this.client.parse<ConfigurationGroup[]>(
19
+ z.array(ConfigurationGroupSchema),
20
+ await this.client.request<unknown>('GET', `get_configs/${projectId}`),
21
+ );
22
+ }
23
+
24
+ async addConfigurationGroup(projectId: number, payload: AddConfigurationGroupPayload): Promise<ConfigurationGroup> {
25
+ this.client.validateId(projectId, 'projectId');
26
+ return this.client.parse<ConfigurationGroup>(
27
+ ConfigurationGroupSchema,
28
+ await this.client.request<unknown>('POST', `add_config_group/${projectId}`, payload),
29
+ );
30
+ }
31
+
32
+ async updateConfigurationGroup(
33
+ configGroupId: number,
34
+ payload: UpdateConfigurationGroupPayload,
35
+ ): Promise<ConfigurationGroup> {
36
+ this.client.validateId(configGroupId, 'configGroupId');
37
+ return this.client.parse<ConfigurationGroup>(
38
+ ConfigurationGroupSchema,
39
+ await this.client.request<unknown>('POST', `update_config_group/${configGroupId}`, payload),
40
+ );
41
+ }
42
+
43
+ async deleteConfigurationGroup(configGroupId: number): Promise<void> {
44
+ this.client.validateId(configGroupId, 'configGroupId');
45
+ await this.client.request<void>('POST', `delete_config_group/${configGroupId}`);
46
+ }
47
+
48
+ async addConfiguration(configGroupId: number, payload: AddConfigurationPayload): Promise<Configuration> {
49
+ this.client.validateId(configGroupId, 'configGroupId');
50
+ return this.client.parse<Configuration>(
51
+ ConfigurationSchema,
52
+ await this.client.request<unknown>('POST', `add_config/${configGroupId}`, payload),
53
+ );
54
+ }
55
+
56
+ async updateConfiguration(configId: number, payload: UpdateConfigurationPayload): Promise<Configuration> {
57
+ this.client.validateId(configId, 'configId');
58
+ return this.client.parse<Configuration>(
59
+ ConfigurationSchema,
60
+ await this.client.request<unknown>('POST', `update_config/${configId}`, payload),
61
+ );
62
+ }
63
+
64
+ async deleteConfiguration(configId: number): Promise<void> {
65
+ this.client.validateId(configId, 'configId');
66
+ await this.client.request<void>('POST', `delete_config/${configId}`);
67
+ }
68
+ }
@@ -0,0 +1,44 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Dataset, AddDatasetPayload, UpdateDatasetPayload } from '../types.js';
3
+ import { DatasetSchema } from '../schemas.js';
4
+
5
+ export class DatasetModule {
6
+ constructor(private readonly client: TestRailClientCore) {}
7
+
8
+ async getDataset(datasetId: number): Promise<Dataset> {
9
+ this.client.validateId(datasetId, 'datasetId');
10
+ return this.client.parse<Dataset>(
11
+ DatasetSchema,
12
+ await this.client.request<unknown>('GET', `get_dataset/${datasetId}`),
13
+ );
14
+ }
15
+
16
+ async getDatasets(projectId: number): Promise<Dataset[]> {
17
+ this.client.validateId(projectId, 'projectId');
18
+ return this.client.parse<Dataset[]>(
19
+ DatasetSchema.array(),
20
+ await this.client.request<unknown>('GET', `get_datasets/${projectId}`),
21
+ );
22
+ }
23
+
24
+ async addDataset(projectId: number, payload: AddDatasetPayload): Promise<Dataset> {
25
+ this.client.validateId(projectId, 'projectId');
26
+ return this.client.parse<Dataset>(
27
+ DatasetSchema,
28
+ await this.client.request<unknown>('POST', `add_dataset/${projectId}`, payload),
29
+ );
30
+ }
31
+
32
+ async updateDataset(datasetId: number, payload: UpdateDatasetPayload): Promise<Dataset> {
33
+ this.client.validateId(datasetId, 'datasetId');
34
+ return this.client.parse<Dataset>(
35
+ DatasetSchema,
36
+ await this.client.request<unknown>('POST', `update_dataset/${datasetId}`, payload),
37
+ );
38
+ }
39
+
40
+ async deleteDataset(datasetId: number): Promise<void> {
41
+ this.client.validateId(datasetId, 'datasetId');
42
+ await this.client.request<void>('POST', `delete_dataset/${datasetId}`);
43
+ }
44
+ }
@@ -0,0 +1,63 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Status, Priority, ResultField, CaseField, CaseType, Template, Role } from '../types.js';
3
+ import {
4
+ StatusSchema,
5
+ PrioritySchema,
6
+ ResultFieldSchema,
7
+ CaseFieldSchema,
8
+ CaseTypeSchema,
9
+ TemplateSchema,
10
+ RoleSchema,
11
+ } from '../schemas.js';
12
+ import { z } from 'zod';
13
+
14
+ export class MetadataModule {
15
+ constructor(private readonly client: TestRailClientCore) {}
16
+
17
+ async getStatuses(): Promise<Status[]> {
18
+ return this.client.parse<Status[]>(
19
+ z.array(StatusSchema),
20
+ await this.client.request<unknown>('GET', 'get_statuses'),
21
+ );
22
+ }
23
+
24
+ async getPriorities(): Promise<Priority[]> {
25
+ return this.client.parse<Priority[]>(
26
+ z.array(PrioritySchema),
27
+ await this.client.request<unknown>('GET', 'get_priorities'),
28
+ );
29
+ }
30
+
31
+ async getResultFields(): Promise<ResultField[]> {
32
+ return this.client.parse<ResultField[]>(
33
+ z.array(ResultFieldSchema),
34
+ await this.client.request<unknown>('GET', 'get_result_fields'),
35
+ );
36
+ }
37
+
38
+ async getCaseFields(): Promise<CaseField[]> {
39
+ return this.client.parse<CaseField[]>(
40
+ z.array(CaseFieldSchema),
41
+ await this.client.request<unknown>('GET', 'get_case_fields'),
42
+ );
43
+ }
44
+
45
+ async getCaseTypes(): Promise<CaseType[]> {
46
+ return this.client.parse<CaseType[]>(
47
+ z.array(CaseTypeSchema),
48
+ await this.client.request<unknown>('GET', 'get_case_types'),
49
+ );
50
+ }
51
+
52
+ async getTemplates(projectId: number): Promise<Template[]> {
53
+ this.client.validateId(projectId, 'projectId');
54
+ return this.client.parse<Template[]>(
55
+ z.array(TemplateSchema),
56
+ await this.client.request<unknown>('GET', `get_templates/${projectId}`),
57
+ );
58
+ }
59
+
60
+ async getRoles(): Promise<Role[]> {
61
+ return this.client.parse<Role[]>(z.array(RoleSchema), await this.client.request<unknown>('GET', 'get_roles'));
62
+ }
63
+ }
@@ -0,0 +1,54 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type { Milestone, GetMilestonesOptions, AddMilestonePayload, UpdateMilestonePayload } from '../types.js';
3
+ import { MilestoneSchema } from '../schemas.js';
4
+ import { z } from 'zod';
5
+
6
+ export class MilestoneModule {
7
+ constructor(private readonly client: TestRailClientCore) {}
8
+
9
+ async getMilestone(milestoneId: number): Promise<Milestone> {
10
+ this.client.validateId(milestoneId, 'milestoneId');
11
+ return this.client.parse<Milestone>(
12
+ MilestoneSchema,
13
+ await this.client.request<unknown>('GET', `get_milestone/${milestoneId}`),
14
+ );
15
+ }
16
+
17
+ async getMilestones(projectId: number, options?: GetMilestonesOptions): Promise<Milestone[]> {
18
+ this.client.validateId(projectId, 'projectId');
19
+ this.client.validatePaginationParams(options?.limit, options?.offset);
20
+ const endpoint = this.client.buildEndpoint(`get_milestones/${projectId}`, {
21
+ is_completed: options?.is_completed,
22
+ limit: options?.limit,
23
+ offset: options?.offset,
24
+ });
25
+ const raw = await this.client.request<unknown>('GET', endpoint);
26
+ return (
27
+ this.client.parse<{ milestones?: Milestone[] }>(
28
+ z.object({ milestones: z.array(MilestoneSchema).optional() }),
29
+ raw,
30
+ ).milestones ?? []
31
+ );
32
+ }
33
+
34
+ async addMilestone(projectId: number, payload: AddMilestonePayload): Promise<Milestone> {
35
+ this.client.validateId(projectId, 'projectId');
36
+ return this.client.parse<Milestone>(
37
+ MilestoneSchema,
38
+ await this.client.request<unknown>('POST', `add_milestone/${projectId}`, payload),
39
+ );
40
+ }
41
+
42
+ async updateMilestone(milestoneId: number, payload: UpdateMilestonePayload): Promise<Milestone> {
43
+ this.client.validateId(milestoneId, 'milestoneId');
44
+ return this.client.parse<Milestone>(
45
+ MilestoneSchema,
46
+ await this.client.request<unknown>('POST', `update_milestone/${milestoneId}`, payload),
47
+ );
48
+ }
49
+
50
+ async deleteMilestone(milestoneId: number): Promise<void> {
51
+ this.client.validateId(milestoneId, 'milestoneId');
52
+ await this.client.request<void>('POST', `delete_milestone/${milestoneId}`);
53
+ }
54
+ }
@@ -0,0 +1,89 @@
1
+ import { TestRailClientCore } from '../client-core.js';
2
+ import type {
3
+ Plan,
4
+ PlanEntry,
5
+ GetPlansOptions,
6
+ AddPlanPayload,
7
+ UpdatePlanPayload,
8
+ AddPlanEntryPayload,
9
+ UpdatePlanEntryPayload,
10
+ } from '../types.js';
11
+ import { PlanSchema, PlanEntrySchema } from '../schemas.js';
12
+ import { serializeIdList } from '../utils.js';
13
+ import { z } from 'zod';
14
+
15
+ export class PlanModule {
16
+ constructor(private readonly client: TestRailClientCore) {}
17
+
18
+ async getPlan(planId: number): Promise<Plan> {
19
+ this.client.validateId(planId, 'planId');
20
+ return this.client.parse<Plan>(PlanSchema, await this.client.request<unknown>('GET', `get_plan/${planId}`));
21
+ }
22
+
23
+ async getPlans(projectId: number, options?: GetPlansOptions): Promise<Plan[]> {
24
+ this.client.validateId(projectId, 'projectId');
25
+ this.client.validatePaginationParams(options?.limit, options?.offset);
26
+ const endpoint = this.client.buildEndpoint(`get_plans/${projectId}`, {
27
+ created_after: options?.created_after,
28
+ created_before: options?.created_before,
29
+ created_by: serializeIdList(options?.created_by),
30
+ is_completed: options?.is_completed,
31
+ milestone_id: serializeIdList(options?.milestone_id),
32
+ limit: options?.limit,
33
+ offset: options?.offset,
34
+ });
35
+ const raw = await this.client.request<unknown>('GET', endpoint);
36
+ return (
37
+ this.client.parse<{ plans?: Plan[] }>(z.object({ plans: z.array(PlanSchema).optional() }), raw).plans ?? []
38
+ );
39
+ }
40
+
41
+ async addPlan(projectId: number, payload: AddPlanPayload): Promise<Plan> {
42
+ this.client.validateId(projectId, 'projectId');
43
+ return this.client.parse<Plan>(
44
+ PlanSchema,
45
+ await this.client.request<unknown>('POST', `add_plan/${projectId}`, payload),
46
+ );
47
+ }
48
+
49
+ async updatePlan(planId: number, payload: UpdatePlanPayload): Promise<Plan> {
50
+ this.client.validateId(planId, 'planId');
51
+ return this.client.parse<Plan>(
52
+ PlanSchema,
53
+ await this.client.request<unknown>('POST', `update_plan/${planId}`, payload),
54
+ );
55
+ }
56
+
57
+ async closePlan(planId: number): Promise<Plan> {
58
+ this.client.validateId(planId, 'planId');
59
+ return this.client.parse<Plan>(PlanSchema, await this.client.request<unknown>('POST', `close_plan/${planId}`));
60
+ }
61
+
62
+ async deletePlan(planId: number): Promise<void> {
63
+ this.client.validateId(planId, 'planId');
64
+ await this.client.request<void>('POST', `delete_plan/${planId}`);
65
+ }
66
+
67
+ async addPlanEntry(planId: number, payload: AddPlanEntryPayload): Promise<PlanEntry> {
68
+ this.client.validateId(planId, 'planId');
69
+ return this.client.parse<PlanEntry>(
70
+ PlanEntrySchema,
71
+ await this.client.request<unknown>('POST', `add_plan_entry/${planId}`, payload),
72
+ );
73
+ }
74
+
75
+ async updatePlanEntry(planId: number, entryId: string, payload: UpdatePlanEntryPayload): Promise<PlanEntry> {
76
+ this.client.validateId(planId, 'planId');
77
+ this.client.validateEntryId(entryId);
78
+ return this.client.parse<PlanEntry>(
79
+ PlanEntrySchema,
80
+ await this.client.request<unknown>('POST', `update_plan_entry/${planId}/${entryId}`, payload),
81
+ );
82
+ }
83
+
84
+ async deletePlanEntry(planId: number, entryId: string): Promise<void> {
85
+ this.client.validateId(planId, 'planId');
86
+ this.client.validateEntryId(entryId);
87
+ await this.client.request<void>('POST', `delete_plan_entry/${planId}/${entryId}`);
88
+ }
89
+ }