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

100% Statements 13/13
100% Branches 4/4
100% Functions 1/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 10717x 17x                         17x     17x     17x           17x         17x             17x                                       17x                     17x                     17x                       17x               9x            
import { z } from 'zod';
import { requiredId, paginationFields } from '../utils';
 
// ============================================================================
// browse_audit_events - CQRS Query Tool (discriminated union schema)
// Actions: list_instance, list_group, list_project, get
//
// Audit events are an immutable compliance record of who did what, when. There is
// no manage_* counterpart - they cannot be created or edited via the API. Exposed
// only through REST. The instance/group/project scopes fold in as actions; get
// infers the scope from project_id / group_id. Gated behind USE_AUDIT_EVENTS.
// Premium/Ultimate tier; list_instance additionally requires admin.
// ============================================================================
 
export const projectIdField = requiredId.describe(
  "Project ID or URL-encoded path (e.g. 'group/project' or '123').",
);
export const groupIdField = requiredId.describe(
  "Group ID or URL-encoded path (e.g. 'my-group' or '42').",
);
export const auditEventIdField = z.coerce
  .number()
  .int()
  .positive()
  .describe('Numeric audit-event ID (from a list action).');
 
const createdAfterField = z
  .string()
  .regex(/^\d{4}-\d{2}-\d{2}$/, 'created_after must be a YYYY-MM-DD date')
  .optional()
  .describe('Return events created on or after this date (YYYY-MM-DD).');
const createdBeforeField = z
  .string()
  .regex(/^\d{4}-\d{2}-\d{2}$/, 'created_before must be a YYYY-MM-DD date')
  .optional()
  .describe('Return events created on or before this date (YYYY-MM-DD).');
 
// --- Action: list_instance (admin-only, instance-wide) ---
const ListInstanceSchema = z.object({
  action: z
    .literal('list_instance')
    .describe('List instance-wide audit events (requires admin; Premium+)'),
  entity_type: z
    .string()
    .optional()
    .describe("Filter by entity type, e.g. 'User', 'Group', 'Project', 'Key'."),
  entity_id: z.coerce
    .number()
    .int()
    .positive()
    .optional()
    .describe('Filter by the numeric ID of the entity (used with entity_type).'),
  created_after: createdAfterField,
  created_before: createdBeforeField,
  ...paginationFields(),
});
 
// --- Action: list_group ---
const ListGroupSchema = z.object({
  action: z
    .literal('list_group')
    .describe('List a group audit events (Premium+, group owner/admin)'),
  group_id: groupIdField,
  created_after: createdAfterField,
  created_before: createdBeforeField,
  ...paginationFields(),
});
 
// --- Action: list_project ---
const ListProjectSchema = z.object({
  action: z
    .literal('list_project')
    .describe('List a project audit events (Premium+, project owner/admin)'),
  project_id: projectIdField,
  created_after: createdAfterField,
  created_before: createdBeforeField,
  ...paginationFields(),
});
 
// --- Action: get (single event; scope inferred from project_id/group_id) ---
const GetAuditEventSchema = z.object({
  action: z
    .literal('get')
    .describe(
      'Get a single audit event by ID. Pass project_id for a project event, group_id for a group event, or neither for an instance event (admin).',
    ),
  audit_event_id: auditEventIdField,
  project_id: requiredId.optional().describe('Set for a project audit event.'),
  group_id: requiredId.optional().describe('Set for a group audit event.'),
});
 
// --- Discriminated union combining all actions ---
export const BrowseAuditEventsSchema = z
  .discriminatedUnion('action', [
    ListInstanceSchema,
    ListGroupSchema,
    ListProjectSchema,
    GetAuditEventSchema,
  ])
  // An audit event belongs to a single scope; project_id and group_id are mutually exclusive.
  .refine((data) => data.action !== 'get' || !(data.project_id && data.group_id), {
    message: 'Pass at most one of project_id or group_id (an event belongs to a single scope)',
    path: ['project_id'],
  });
 
export type BrowseAuditEventsInput = z.infer<typeof BrowseAuditEventsSchema>;