From 4ac9d37446903926d7fc97a96082efca240228f4 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 28 Apr 2026 19:29:04 +0000 Subject: [PATCH] refactor: move mutation input typing into services Refs #195 --- src/commands/initiatives/entity.ts | 8 +++--- src/commands/issues.ts | 12 ++++----- src/commands/projects.ts | 7 ++--- src/services/initiative-service.ts | 30 +++++++++++++++++++--- src/services/issue-service.ts | 41 +++++++++++++++++++++++++++--- src/services/project-service.ts | 38 ++++++++++++++++++++++++--- 6 files changed, 110 insertions(+), 26 deletions(-) diff --git a/src/commands/initiatives/entity.ts b/src/commands/initiatives/entity.ts index 261bd13c..4b2318e1 100644 --- a/src/commands/initiatives/entity.ts +++ b/src/commands/initiatives/entity.ts @@ -9,10 +9,8 @@ import { parseLimit, } from "../../common/output.js"; import { - type InitiativeCreateInput, type InitiativeSortInput, InitiativeStatus, - type InitiativeUpdateInput, type ListInitiativesQueryVariables, PaginationNulls, PaginationOrderBy, @@ -40,10 +38,12 @@ import { } from "../../services/discussion-service.js"; import { archiveInitiative, + type CreateInitiativeInput, createInitiative, deleteInitiative, getInitiative, listInitiatives, + type UpdateInitiativeInput, unarchiveInitiative, updateInitiative, } from "../../services/initiative-service.js"; @@ -853,7 +853,7 @@ export function setupInitiativeEntityCommands(initiatives: Command): void { ]; const ctx = createContext(getRootOpts(command)); - const input: InitiativeCreateInput = { name }; + const input: CreateInitiativeInput = { name }; if (options.description !== undefined) { input.description = options.description; @@ -906,7 +906,7 @@ export function setupInitiativeEntityCommands(initiatives: Command): void { const ctx = createContext(getRootOpts(command)); const initiativeId = await resolveInitiativeId(ctx.sdk, initiative); - const input: InitiativeUpdateInput = {}; + const input: UpdateInitiativeInput = {}; if (options.name !== undefined) { input.name = options.name; diff --git a/src/commands/issues.ts b/src/commands/issues.ts index 8d8080e9..aad6c7b0 100644 --- a/src/commands/issues.ts +++ b/src/commands/issues.ts @@ -17,11 +17,7 @@ import { import { handleCommand, outputSuccess, parseLimit } from "../common/output.js"; import { resolveFilterOptions } from "../common/resolve-filters.js"; import { type DomainMeta, formatDomainUsage } from "../common/usage.js"; -import { - type IssueCreateInput, - IssueRelationType, - type IssueUpdateInput, -} from "../gql/graphql.js"; +import { IssueRelationType } from "../gql/graphql.js"; import { resolveCycleId } from "../resolvers/cycle-resolver.js"; import { resolveIssueEstimateContext, @@ -61,6 +57,7 @@ import { } from "../services/issue-relation-service.js"; import { archiveIssue, + type CreateIssueInput, createIssue, deleteIssue, getIssue, @@ -75,6 +72,7 @@ import { getIssueWithReactions, listIssues, searchIssues, + type UpdateIssueInput, unarchiveIssue, updateIssue, } from "../services/issue-service.js"; @@ -1032,7 +1030,7 @@ export function setupIssuesCommands(program: Command): void { }); } - const input: IssueCreateInput = { + const input: CreateIssueInput = { title, teamId, }; @@ -1237,7 +1235,7 @@ export function setupIssuesCommands(program: Command): void { ? await getIssue(ctx.gql, resolvedIssueId) : undefined; - const input: IssueUpdateInput = {}; + const input: UpdateIssueInput = {}; if (options.title) { input.title = options.title; diff --git a/src/commands/projects.ts b/src/commands/projects.ts index 3bcddcc8..673df7d2 100644 --- a/src/commands/projects.ts +++ b/src/commands/projects.ts @@ -4,7 +4,6 @@ import { resolveReactionEmojiInput } from "../common/emoji.js"; import { invalidParameterError } from "../common/errors.js"; import { handleCommand, outputSuccess, parseLimit } from "../common/output.js"; import { type DomainMeta, formatDomainUsage } from "../common/usage.js"; -import type { ProjectCreateInput, ProjectUpdateInput } from "../gql/graphql.js"; import { resolveProjectId, resolveProjectLabelIds, @@ -31,10 +30,12 @@ import { } from "../services/discussion-service.js"; import { archiveProject, + type CreateProjectInput, createProject, deleteProject, getProject, listProjects, + type UpdateProjectInput, unarchiveProject, updateProject, } from "../services/project-service.js"; @@ -526,7 +527,7 @@ export function setupProjectsCommands(program: Command): void { teamNames.map((t) => resolveTeamId(ctx.sdk, t)), ); - const input: ProjectCreateInput = { + const input: CreateProjectInput = { name, teamIds, }; @@ -610,7 +611,7 @@ export function setupProjectsCommands(program: Command): void { const projectId = await resolveProjectId(ctx.sdk, project); - const input: ProjectUpdateInput = {}; + const input: UpdateProjectInput = {}; if (options.name) { input.name = options.name; diff --git a/src/services/initiative-service.ts b/src/services/initiative-service.ts index 073f5fe9..7f09d1f1 100644 --- a/src/services/initiative-service.ts +++ b/src/services/initiative-service.ts @@ -30,6 +30,26 @@ import { type UpdateInitiativeMutation, } from "../gql/graphql.js"; +export interface CreateInitiativeInput { + name: string; + description?: string; + content?: string; + ownerId?: string; + status?: InitiativeCreateInput["status"]; + targetDate?: string; + sortOrder?: number; +} + +export interface UpdateInitiativeInput { + name?: string; + description?: string; + content?: string; + ownerId?: string; + status?: InitiativeUpdateInput["status"]; + targetDate?: string; + sortOrder?: number; +} + export interface InitiativeListOptions { limit?: number; after?: string; @@ -90,12 +110,13 @@ export async function getInitiative( export async function createInitiative( client: GraphQLClient, - input: InitiativeCreateInput, + input: CreateInitiativeInput, ): Promise { + const graphqlInput: InitiativeCreateInput = { ...input }; const result = await client.request( CreateInitiativeDocument, { - input, + input: graphqlInput, }, ); @@ -109,7 +130,7 @@ export async function createInitiative( export async function updateInitiative( client: GraphQLClient, id: string, - input: InitiativeUpdateInput, + input: UpdateInitiativeInput, ): Promise { const hasAtLeastOneField = Object.values(input).some( (value) => value !== undefined, @@ -122,11 +143,12 @@ export async function updateInitiative( ); } + const graphqlInput: InitiativeUpdateInput = { ...input }; const result = await client.request( UpdateInitiativeDocument, { id, - input, + input: graphqlInput, }, ); diff --git a/src/services/issue-service.ts b/src/services/issue-service.ts index 7f4e0adf..f8b398dd 100644 --- a/src/services/issue-service.ts +++ b/src/services/issue-service.ts @@ -58,6 +58,37 @@ import { } from "../gql/graphql.js"; import { normalizeReactions } from "./reaction-service.js"; +export interface CreateIssueInput { + title: string; + teamId: string; + description?: string; + assigneeId?: string; + priority?: number; + estimate?: number; + projectId?: string; + labelIds?: string[]; + projectMilestoneId?: string; + cycleId?: string; + stateId?: string; + parentId?: string; + dueDate?: string; +} + +export interface UpdateIssueInput { + title?: string; + description?: string; + stateId?: string; + priority?: number; + estimate?: number | null; + assigneeId?: string; + projectId?: string; + labelIds?: string[]; + parentId?: string | null; + projectMilestoneId?: string | null; + cycleId?: string | null; + dueDate?: string | null; +} + const NON_COMPLETED_ISSUES_FILTER: IssueFilter = { state: { type: { neq: "completed" } }, }; @@ -396,11 +427,12 @@ export async function searchIssues( export async function createIssue( client: GraphQLClient, - input: IssueCreateInput, + input: CreateIssueInput, ): Promise { + const graphqlInput: IssueCreateInput = { ...input }; const result = await client.request( CreateIssueDocument, - { input }, + { input: graphqlInput }, ); if (!result.issueCreate.success || !result.issueCreate.issue) { throw new Error("Failed to create issue"); @@ -411,11 +443,12 @@ export async function createIssue( export async function updateIssue( client: GraphQLClient, id: string, - input: IssueUpdateInput, + input: UpdateIssueInput, ): Promise { + const graphqlInput: IssueUpdateInput = { ...input }; const result = await client.request( UpdateIssueDocument, - { id, input }, + { id, input: graphqlInput }, ); if (!result.issueUpdate.success || !result.issueUpdate.issue) { throw new Error("Failed to update issue"); diff --git a/src/services/project-service.ts b/src/services/project-service.ts index 38eb27c8..231cb6a6 100644 --- a/src/services/project-service.ts +++ b/src/services/project-service.ts @@ -29,6 +29,34 @@ import { type UpdateProjectMutation, } from "../gql/graphql.js"; +export interface CreateProjectInput { + name: string; + teamIds: string[]; + description?: string; + content?: string; + leadId?: string; + memberIds?: string[]; + priority?: number; + statusId?: string; + startDate?: string; + targetDate?: string; + labelIds?: string[]; +} + +export interface UpdateProjectInput { + name?: string; + description?: string; + content?: string; + leadId?: string; + memberIds?: string[]; + priority?: number; + statusId?: string; + startDate?: string; + targetDate?: string; + teamIds?: string[]; + labelIds?: string[]; +} + export async function listProjects( client: GraphQLClient, options: PaginationOptions = {}, @@ -62,11 +90,12 @@ export async function getProject( export async function createProject( client: GraphQLClient, - input: ProjectCreateInput, + input: CreateProjectInput, ): Promise { + const graphqlInput: ProjectCreateInput = { ...input }; const result = await client.request( CreateProjectDocument, - { input }, + { input: graphqlInput }, ); if (!result.projectCreate.success || !result.projectCreate.project) { @@ -79,11 +108,12 @@ export async function createProject( export async function updateProject( client: GraphQLClient, id: string, - input: ProjectUpdateInput, + input: UpdateProjectInput, ): Promise { + const graphqlInput: ProjectUpdateInput = { ...input }; const result = await client.request( UpdateProjectDocument, - { id, input }, + { id, input: graphqlInput }, ); if (!result.projectUpdate.success || !result.projectUpdate.project) {