Skip to content

Commit 30af904

Browse files
committed
fix: properly type worklog API interactions
- Add proper TypeScript interfaces for worklog data structures - Replace any types with specific interfaces (WorklogCreateData, WorklogUpdateData, WorklogDeleteParams) - Add type-safe worklog API parameter interfaces in controller - Export and use IssueWorklog and IssueWorklogContainer types - Validate API responses against proper Zod schemas - Ensure all worklog operations are fully type-safe This eliminates the need for eslint-disable comments and provides full type safety for worklog operations.
1 parent 2cf1db3 commit 30af904

3 files changed

Lines changed: 88 additions & 26 deletions

File tree

src/controllers/atlassian.worklogs.controller.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,25 @@ import { formatPagination } from '../utils/formatter.util.js';
2828
import { markdownToAdf } from '../utils/adf-from-markdown.util.js';
2929
import { textToAdf } from '../utils/adf-from-text.util.js';
3030

31+
// Types for worklog API parameters
32+
interface WorklogApiParams {
33+
timeSpentSeconds?: number;
34+
started?: string;
35+
comment?: string | { type: string; version: number; content: unknown[] };
36+
visibility?: {
37+
type: string;
38+
identifier?: string;
39+
value?: string;
40+
};
41+
}
42+
43+
interface WorklogEstimateParams {
44+
adjustEstimate?: string;
45+
newEstimate?: string;
46+
reduceBy?: string;
47+
increaseBy?: string;
48+
}
49+
3150
// Create a contextualized logger for this file
3251
const controllerLogger = Logger.forContext(
3352
'controllers/atlassian.worklogs.controller.ts',
@@ -124,7 +143,7 @@ async function listWorklogs(
124143
);
125144

126145
methodLogger.debug(
127-
`Retrieved ${worklogsData.worklogs.length} worklogs out of ${worklogsData.total} total`,
146+
`Retrieved ${worklogsData.worklogs?.length || 0} worklogs out of ${worklogsData.total || 0} total`,
128147
);
129148

130149
// Extract pagination info
@@ -136,7 +155,7 @@ async function listWorklogs(
136155

137156
// Format worklogs
138157
const formattedWorklogs = formatWorklogsList(
139-
worklogsData.worklogs,
158+
worklogsData.worklogs || [],
140159
mergedOptions.issueIdOrKey,
141160
);
142161

@@ -213,8 +232,7 @@ async function addWorklog(
213232
}
214233

215234
// Build request parameters
216-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
217-
const params: any = {
235+
const params: WorklogApiParams & WorklogEstimateParams = {
218236
timeSpentSeconds,
219237
started: options.started,
220238
};
@@ -295,8 +313,7 @@ async function updateWorklog(
295313
}
296314

297315
// Build update parameters
298-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
299-
const params: any = {};
316+
const params: WorklogApiParams = {};
300317

301318
// Convert time spent to seconds if provided
302319
if (options.timeSpent) {
@@ -388,8 +405,7 @@ async function deleteWorklog(
388405
}
389406

390407
// Build delete parameters
391-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
392-
const params: any = {};
408+
const params: WorklogEstimateParams = {};
393409

394410
// Handle estimate adjustment
395411
if (options.adjustEstimate) {

src/services/vendor.atlassian.issues.service.ts

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ import {
1515
IssueCommentSchema,
1616
ListCommentsParams,
1717
AddCommentParams,
18+
IssueWorklog,
19+
IssueWorklogContainer,
20+
IssueWorklogContainerSchema,
21+
IssueWorklogSchema,
1822
} from './vendor.atlassian.issues.types.js';
1923
import {
2024
createAuthMissingError,
@@ -24,6 +28,40 @@ import {
2428
import { validateResponse } from '../utils/validation.util.js';
2529
import { z } from 'zod';
2630

31+
// Worklog types for API interactions
32+
interface WorklogCreateData {
33+
timeSpentSeconds?: number;
34+
timeSpent?: string;
35+
started?: string;
36+
comment?: string | { type: string; version: number; content: unknown[] };
37+
visibility?: {
38+
type: string;
39+
identifier?: string;
40+
value?: string;
41+
};
42+
adjustEstimate?: string;
43+
newEstimate?: string;
44+
reduceBy?: string;
45+
}
46+
47+
interface WorklogUpdateData {
48+
timeSpentSeconds?: number;
49+
timeSpent?: string;
50+
started?: string;
51+
comment?: string | { type: string; version: number; content: unknown[] };
52+
visibility?: {
53+
type: string;
54+
identifier?: string;
55+
value?: string;
56+
};
57+
}
58+
59+
interface WorklogDeleteParams {
60+
adjustEstimate?: string;
61+
newEstimate?: string;
62+
increaseBy?: string;
63+
}
64+
2765
// Create a contextualized logger for this file
2866
const serviceLogger = Logger.forContext(
2967
'services/vendor.atlassian.issues.service.ts',
@@ -468,7 +506,7 @@ async function addComment(
468506
* @param {number} [params.startAt] - Pagination start index
469507
* @param {number} [params.maxResults] - Maximum number of results to return
470508
* @param {string[]} [params.expand] - Worklog data to expand in response
471-
* @returns {Promise<any>} Promise containing the worklogs with pagination information
509+
* @returns {Promise<IssueWorklogContainer>} Promise containing the worklogs with pagination information
472510
* @throws {Error} If Atlassian credentials are missing or API request fails
473511
* @example
474512
* // Get worklogs for an issue with pagination
@@ -484,7 +522,7 @@ async function getWorklogs(
484522
maxResults?: number;
485523
expand?: string[];
486524
} = {},
487-
): Promise<unknown> {
525+
): Promise<IssueWorklogContainer> {
488526
const methodLogger = Logger.forContext(
489527
'services/vendor.atlassian.issues.service.ts',
490528
'getWorklogs',
@@ -526,9 +564,12 @@ async function getWorklogs(
526564

527565
try {
528566
const rawData = await fetchAtlassian(credentials, path);
529-
// For worklogs, we don't have a specific schema validation yet
530-
// so we'll return the raw data
531-
return rawData;
567+
// Validate response against schema
568+
return validateResponse(
569+
rawData,
570+
IssueWorklogContainerSchema,
571+
'getWorklogs',
572+
);
532573
} catch (error) {
533574
// McpError is already properly structured from fetchAtlassian
534575
if (error instanceof McpError) {
@@ -557,8 +598,8 @@ async function getWorklogs(
557598
* @async
558599
* @memberof VendorAtlassianIssuesService
559600
* @param {string} issueIdOrKey - The ID or key of the issue to add a worklog to
560-
* @param {any} worklogData - Parameters for the worklog to add
561-
* @returns {Promise<any>} Promise containing the created worklog information
601+
* @param {WorklogCreateData} worklogData - Parameters for the worklog to add
602+
* @returns {Promise<IssueWorklog>} Promise containing the created worklog information
562603
* @throws {Error} If Atlassian credentials are missing or API request fails
563604
* @example
564605
* // Add a worklog with time spent and comment
@@ -570,8 +611,8 @@ async function getWorklogs(
570611
*/
571612
async function addWorklog(
572613
issueIdOrKey: string,
573-
worklogData: any, // eslint-disable-line @typescript-eslint/no-explicit-any
574-
): Promise<unknown> {
614+
worklogData: WorklogCreateData,
615+
): Promise<IssueWorklog> {
575616
const methodLogger = Logger.forContext(
576617
'services/vendor.atlassian.issues.service.ts',
577618
'addWorklog',
@@ -629,7 +670,8 @@ async function addWorklog(
629670
body: requestBody,
630671
});
631672

632-
return response;
673+
// Validate response against schema
674+
return validateResponse(response, IssueWorklogSchema, 'addWorklog');
633675
} catch (error) {
634676
// McpError is already properly structured from fetchAtlassian
635677
if (error instanceof McpError) {
@@ -658,15 +700,15 @@ async function addWorklog(
658700
* @memberof VendorAtlassianIssuesService
659701
* @param {string} issueIdOrKey - The ID or key of the issue
660702
* @param {string} worklogId - The ID of the worklog to update
661-
* @param {any} updateData - Parameters for the worklog update
662-
* @returns {Promise<any>} Promise containing the updated worklog information
703+
* @param {WorklogUpdateData} updateData - Parameters for the worklog update
704+
* @returns {Promise<IssueWorklog>} Promise containing the updated worklog information
663705
* @throws {Error} If Atlassian credentials are missing or API request fails
664706
*/
665707
async function updateWorklog(
666708
issueIdOrKey: string,
667709
worklogId: string,
668-
updateData: any, // eslint-disable-line @typescript-eslint/no-explicit-any
669-
): Promise<unknown> {
710+
updateData: WorklogUpdateData,
711+
): Promise<IssueWorklog> {
670712
const methodLogger = Logger.forContext(
671713
'services/vendor.atlassian.issues.service.ts',
672714
'updateWorklog',
@@ -693,7 +735,8 @@ async function updateWorklog(
693735
body: updateData,
694736
});
695737

696-
return response;
738+
// Validate response against schema
739+
return validateResponse(response, IssueWorklogSchema, 'updateWorklog');
697740
} catch (error) {
698741
if (error instanceof McpError) {
699742
throw error;
@@ -720,14 +763,14 @@ async function updateWorklog(
720763
* @memberof VendorAtlassianIssuesService
721764
* @param {string} issueIdOrKey - The ID or key of the issue
722765
* @param {string} worklogId - The ID of the worklog to delete
723-
* @param {any} [params={}] - Optional parameters for estimate adjustment
766+
* @param {WorklogDeleteParams} [params={}] - Optional parameters for estimate adjustment
724767
* @returns {Promise<void>} Promise that resolves when the worklog is deleted
725768
* @throws {Error} If Atlassian credentials are missing or API request fails
726769
*/
727770
async function deleteWorklog(
728771
issueIdOrKey: string,
729772
worklogId: string,
730-
params: any = {}, // eslint-disable-line @typescript-eslint/no-explicit-any
773+
params: WorklogDeleteParams = {},
731774
): Promise<void> {
732775
const methodLogger = Logger.forContext(
733776
'services/vendor.atlassian.issues.service.ts',

src/services/vendor.atlassian.issues.types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ export const IssueWorklogSchema = z.object({
210210
/**
211211
* Issue worklog container schema - Jira API sometimes returns this as an object with nested arrays instead of a direct array
212212
*/
213-
const IssueWorklogContainerSchema = z
213+
export const IssueWorklogContainerSchema = z
214214
.object({
215215
worklogs: z.array(IssueWorklogSchema).optional(),
216216
maxResults: z.number().optional(),
@@ -219,6 +219,9 @@ const IssueWorklogContainerSchema = z
219219
})
220220
.passthrough();
221221

222+
export type IssueWorklog = z.infer<typeof IssueWorklogSchema>;
223+
export type IssueWorklogContainer = z.infer<typeof IssueWorklogContainerSchema>;
224+
222225
/**
223226
* Issue time tracking schema
224227
*/

0 commit comments

Comments
 (0)