mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-11-14 00:01:03 +00:00
feat(journey): update_journey takes every setting REST does and answers what it replaced
The MCP tool lacked show_trip_tracks and cover_gradient, which PATCH /api/journeys/:id has always taken. Both now read the input through one helper, and the tool goes through a small settings service that writes the same patch in a transaction with the stored values it replaces, answered as previous, so a change can be written back. The subtitle and the cover gradient take null to clear them. The tool's inline zod moves into strict schemas in @trek/shared; the REST route and its body are unchanged.
This commit is contained in:
@@ -12,6 +12,8 @@ import {
|
||||
journeyEntryProsConsSchema,
|
||||
journeyEntryTypeSchema,
|
||||
journeyEntryVisibilitySchema,
|
||||
journeyCreateToolSchema,
|
||||
journeyUpdateToolSchema,
|
||||
} from './journey.schema';
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
@@ -140,3 +142,22 @@ describe('the entry enums and the range of a read', () => {
|
||||
expect(journeyEntriesRangeSchema.safeParse({ to: 'yesterday' }).success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('the journey tool arguments', () => {
|
||||
it('JSCHEMA-TOOL-001: a new journey needs a title and refuses what the tool does not take', () => {
|
||||
expect(journeyCreateToolSchema.parse({ title: 'Japan', trip_ids: [5] })).toEqual({ title: 'Japan', trip_ids: [5] });
|
||||
expect(journeyCreateToolSchema.safeParse({ title: '' }).success).toBe(false);
|
||||
expect(journeyCreateToolSchema.safeParse({ title: 'x'.repeat(201) }).success).toBe(false);
|
||||
expect(journeyCreateToolSchema.safeParse({ title: 'Japan', cover: 'x' }).success).toBe(false);
|
||||
});
|
||||
|
||||
it('JSCHEMA-TOOL-002: an update takes the trip tracks and the cover gradient, and null for the subtitle', () => {
|
||||
const update = { show_trip_tracks: true, cover_gradient: null, subtitle: null, status_override: null };
|
||||
expect(journeyUpdateToolSchema.parse(update)).toEqual(update);
|
||||
expect(journeyUpdateToolSchema.safeParse({ status: 'upcoming' }).success).toBe(false);
|
||||
expect(journeyUpdateToolSchema.safeParse({ status_override: 'active' }).success).toBe(false);
|
||||
expect(journeyUpdateToolSchema.safeParse({ title: null }).success).toBe(false);
|
||||
expect(journeyUpdateToolSchema.safeParse({ show_mood: 'yes' }).success).toBe(false);
|
||||
expect(journeyUpdateToolSchema.safeParse({ cover_gradient: 'x'.repeat(201) }).success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { idSchema } from '../common/primitives.schema';
|
||||
import { journeyRecordingDateSchema } from './journey-recordings.schema';
|
||||
|
||||
import { z } from 'zod';
|
||||
@@ -49,6 +50,58 @@ export const journeyUpdateRequestSchema = z.looseObject({
|
||||
});
|
||||
export type JourneyUpdateRequest = z.infer<typeof journeyUpdateRequestSchema>;
|
||||
|
||||
/** A journey's stored states, and the state shown in place of the one its trips' dates give (#762). */
|
||||
export const JOURNEY_STATUSES = ['draft', 'active', 'completed', 'archived'] as const;
|
||||
export const journeyStatusSchema = z.enum(JOURNEY_STATUSES);
|
||||
export const JOURNEY_STATUS_OVERRIDES = ['draft', 'live', 'completed'] as const;
|
||||
export const journeyStatusOverrideSchema = z.enum(JOURNEY_STATUS_OVERRIDES);
|
||||
|
||||
/**
|
||||
* The new journey the MCP tool `create_journey` takes. Strict, unlike the
|
||||
* REST body above, whose bespoke 'Title is required' is pinned: a tool's
|
||||
* arguments are checked here and nowhere else.
|
||||
*/
|
||||
export const journeyCreateToolSchema = z.strictObject({
|
||||
title: z.string().min(1).max(200),
|
||||
subtitle: z.string().max(300).optional(),
|
||||
trip_ids: z.array(idSchema).optional(),
|
||||
});
|
||||
export type JourneyCreateTool = z.infer<typeof journeyCreateToolSchema>;
|
||||
|
||||
/**
|
||||
* The settings the MCP tool `update_journey` changes, every one the REST
|
||||
* PATCH takes but the cover image (an upload). The subtitle and the cover
|
||||
* gradient take null to clear them, so a change can always be written back.
|
||||
*/
|
||||
export const journeyUpdateToolSchema = z.strictObject({
|
||||
title: z.string().min(1).max(200).optional(),
|
||||
subtitle: z.string().max(300).nullable().optional(),
|
||||
status: journeyStatusSchema.optional(),
|
||||
status_override: journeyStatusOverrideSchema
|
||||
.nullable()
|
||||
.optional()
|
||||
.describe('Shown state set by hand, or null to follow the trip dates'),
|
||||
show_verdict: z.boolean().optional().describe('Whether entries in this journey offer a pros/cons list'),
|
||||
show_mood: z.boolean().optional().describe('Whether entries in this journey offer a mood'),
|
||||
show_weather: z.boolean().optional().describe('Whether entries in this journey offer a weather note'),
|
||||
photo_location: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe('Whether an entry without a place takes the position of the first geotagged photo added to it'),
|
||||
auto_prepare_recordings: journeyUpdateRequestSchema.shape.auto_prepare_recordings,
|
||||
show_trip_tracks: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.describe("Whether the journey's map draws the GPX tracks of its trips' places"),
|
||||
cover_gradient: z
|
||||
.string()
|
||||
.max(200)
|
||||
.nullable()
|
||||
.optional()
|
||||
.describe('A CSS gradient the cover shows while it has no image; null clears it'),
|
||||
});
|
||||
export type JourneyUpdateTool = z.infer<typeof journeyUpdateToolSchema>;
|
||||
|
||||
export const journeyAddTripRequestSchema = z.looseObject({
|
||||
// Handler answers 'trip_id required'.
|
||||
trip_id: z.unknown().optional(),
|
||||
|
||||
Reference in New Issue
Block a user