Skip to content

Commit 31d5efe

Browse files
committedMar 31, 2025
fix: add parent-child relationship support for createWorkItem
1 parent 28ed7a7 commit 31d5efe

File tree

5 files changed

+86
-0
lines changed

5 files changed

+86
-0
lines changed
 

‎src/features/work-items/create-work-item/feature.spec.int.ts

+68
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,72 @@ describe('createWorkItem integration', () => {
113113
expect(result.fields['System.Tags']).toContain('Automated');
114114
}
115115
});
116+
117+
test('should create a child work item with parent-child relationship', async () => {
118+
// Skip if no connection is available
119+
if (shouldSkipIntegrationTest()) {
120+
return;
121+
}
122+
123+
// This connection must be available if we didn't skip
124+
if (!connection) {
125+
throw new Error(
126+
'Connection should be available when test is not skipped',
127+
);
128+
}
129+
130+
// For a true integration test, use a real project
131+
const projectName =
132+
process.env.AZURE_DEVOPS_DEFAULT_PROJECT || 'DefaultProject';
133+
134+
// First, create a parent work item (User Story)
135+
const parentTitle = `Parent Story ${new Date().toISOString()}`;
136+
const parentOptions: CreateWorkItemOptions = {
137+
title: parentTitle,
138+
description: 'This is a parent user story',
139+
};
140+
141+
const parentResult = await createWorkItem(
142+
connection,
143+
projectName,
144+
'User Story', // Assuming User Story type exists
145+
parentOptions,
146+
);
147+
148+
expect(parentResult).toBeDefined();
149+
expect(parentResult.id).toBeDefined();
150+
const parentId = parentResult.id;
151+
152+
// Now create a child work item (Task) with a link to the parent
153+
const childTitle = `Child Task ${new Date().toISOString()}`;
154+
const childOptions: CreateWorkItemOptions = {
155+
title: childTitle,
156+
description: 'This is a child task of a user story',
157+
parentId: parentId, // Reference to parent work item
158+
};
159+
160+
const childResult = await createWorkItem(
161+
connection,
162+
projectName,
163+
'Task',
164+
childOptions,
165+
);
166+
167+
// Assert the child work item was created
168+
expect(childResult).toBeDefined();
169+
expect(childResult.id).toBeDefined();
170+
171+
// Now verify the parent-child relationship
172+
// We would need to fetch the relations, but for now we'll just assert
173+
// that the response indicates a relationship was created
174+
expect(childResult.relations).toBeDefined();
175+
176+
// Check that at least one relation exists that points to our parent
177+
const parentRelation = childResult.relations?.find(
178+
relation =>
179+
relation.rel === 'System.LinkTypes.Hierarchy-Reverse' &&
180+
relation.url && relation.url.includes(`/${parentId}`)
181+
);
182+
expect(parentRelation).toBeDefined();
183+
});
116184
});

‎src/features/work-items/create-work-item/feature.ts

+12
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ export async function createWorkItem(
7575
});
7676
}
7777

78+
// Add parent relationship if parentId is provided
79+
if (options.parentId) {
80+
document.push({
81+
op: 'add',
82+
path: '/relations/-',
83+
value: {
84+
rel: 'System.LinkTypes.Hierarchy-Reverse',
85+
url: `${connection.serverUrl}/_apis/wit/workItems/${options.parentId}`,
86+
},
87+
});
88+
}
89+
7890
// Add any additional fields
7991
if (options.additionalFields) {
8092
for (const [key, value] of Object.entries(options.additionalFields)) {

‎src/features/work-items/schemas.ts

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ export const CreateWorkItemSchema = z.object({
4444
.optional()
4545
.describe('The iteration path for the work item'),
4646
priority: z.number().optional().describe('The priority of the work item'),
47+
parentId: z
48+
.number()
49+
.optional()
50+
.describe('The ID of the parent work item to create a relationship with'),
4751
additionalFields: z
4852
.record(z.string(), z.any())
4953
.optional()

‎src/features/work-items/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export interface CreateWorkItemOptions {
2525
areaPath?: string;
2626
iterationPath?: string;
2727
priority?: number;
28+
parentId?: number;
2829
additionalFields?: Record<string, string | number | boolean | null>;
2930
}
3031

‎src/server.ts

+1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ export function createAzureDevOpsServer(config: AzureDevOpsConfig): Server {
198198
areaPath: args.areaPath,
199199
iterationPath: args.iterationPath,
200200
priority: args.priority,
201+
parentId: args.parentId,
201202
additionalFields: args.additionalFields,
202203
},
203204
);

0 commit comments

Comments
 (0)
Please sign in to comment.