All files / src/entities/milestones schema-readonly.ts

100% Statements 13/13
100% Branches 0/0
0% Functions 0/1
100% Lines 13/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 11817x 17x 17x     17x     17x                                             17x             17x         17x                                                         17x             17x               17x               17x               17x                            
import { z } from "zod";
import { GitLabMilestoneSchema } from "../shared";
import { flexibleBoolean, requiredId, paginationFields } from "../utils";
 
// Re-export shared schema
export { GitLabMilestoneSchema };
 
// Milestones rest api output schemas
export const GitLabMilestonesSchema = z.object({
  id: z.coerce.string(),
  iid: z.coerce.string(),
  project_id: z.coerce.string(),
  title: z.string(),
  description: z.string().nullable(),
  due_date: z.string().nullable(),
  start_date: z.string().nullable(),
  state: z.string(),
  updated_at: z.string(),
  created_at: z.string(),
  expired: flexibleBoolean,
  web_url: z.string().optional(),
});
 
// ============================================================================
// browse_milestones - CQRS Query Tool (discriminated union schema)
// Actions: list, get, issues, merge_requests, burndown
// Uses z.discriminatedUnion() for type-safe action handling.
// Schema pipeline flattens to flat JSON Schema for AI clients that don't support oneOf.
// ============================================================================
 
// --- Shared fields ---
const namespaceField = z.string().describe("Namespace path (group or project)");
 
// NOTE on milestone_id:
// GitLab Milestones REST API uses the global ID in URL paths, NOT the IID.
// Example: GET /projects/:id/milestones/:milestone_id where :milestone_id is the global ID.
// The API response contains both 'id' (global unique) and 'iid' (project-scoped).
// Unlike issues/MRs which use IID in URLs, milestones use the global ID.
const milestoneIdField = requiredId.describe(
  "The ID of a project or group milestone. Required for 'get', 'issues', 'merge_requests', 'burndown' action(s)."
);
 
// --- Action: list ---
const ListMilestonesSchema = z.object({
  action: z.literal("list").describe("List milestones with optional filtering"),
  namespace: namespaceField,
  iids: z.array(z.string()).optional().describe("Return only the milestones having the given iid"),
  state: z
    .enum(["active", "closed"])
    .optional()
    .describe("Return only active or closed milestones"),
  title: z
    .string()
    .optional()
    .describe("Return only milestones with a title matching the provided string"),
  search: z
    .string()
    .optional()
    .describe("Return only milestones with a title or description matching the provided string"),
  include_ancestors: flexibleBoolean.optional().describe("Include ancestor groups"),
  updated_before: z
    .string()
    .optional()
    .describe("Return milestones updated before the specified date (ISO 8601 format)"),
  updated_after: z
    .string()
    .optional()
    .describe("Return milestones updated after the specified date (ISO 8601 format)"),
  ...paginationFields(),
});
 
// --- Action: get ---
const GetMilestoneSchema = z.object({
  action: z.literal("get").describe("Get a single milestone by ID"),
  namespace: namespaceField,
  milestone_id: milestoneIdField,
});
 
// --- Action: issues ---
const MilestoneIssuesSchema = z.object({
  action: z.literal("issues").describe("List issues assigned to a milestone"),
  namespace: namespaceField,
  milestone_id: milestoneIdField,
  ...paginationFields(),
});
 
// --- Action: merge_requests ---
const MilestoneMergeRequestsSchema = z.object({
  action: z.literal("merge_requests").describe("List merge requests assigned to a milestone"),
  namespace: namespaceField,
  milestone_id: milestoneIdField,
  ...paginationFields(),
});
 
// --- Action: burndown ---
const MilestoneBurndownSchema = z.object({
  action: z.literal("burndown").describe("Get burndown chart data for a milestone"),
  namespace: namespaceField,
  milestone_id: milestoneIdField,
  ...paginationFields(),
});
 
// --- Discriminated union combining all actions ---
export const BrowseMilestonesSchema = z.discriminatedUnion("action", [
  ListMilestonesSchema,
  GetMilestoneSchema,
  MilestoneIssuesSchema,
  MilestoneMergeRequestsSchema,
  MilestoneBurndownSchema,
]);
 
// ============================================================================
// Type exports
// ============================================================================
 
export type BrowseMilestonesInput = z.infer<typeof BrowseMilestonesSchema>;
export type GitLabMilestones = z.infer<typeof GitLabMilestonesSchema>;