From f965f06b9c088e47bc6aeb442b5d6d9b6bc53367 Mon Sep 17 00:00:00 2001 From: Nashy Bot <264955517+nashysolutions-bot@users.noreply.github.com> Date: Mon, 21 Sep 2026 07:29:57 +0100 Subject: [PATCH] Map EKEvent.notes to Event.description `Event` had no property for an event's notes, so `EKEvent.notes` was dropped on conversion. Downstream in iMCP this made notes write-only: `events_create` accepts a `notes` argument and sets it on the event, but `events_fetch` returns `Ontology.Event`, so the field could never be read back. Mirrors the existing treatment of the Reminders equivalent, where `PlanAction.init(_ reminder: EKReminder)` maps `reminder.notes` to `description`. `description` is a valid property on schema.org/Event, so the JSON-LD spelling matches. Adds two tests: one that the mapping happens and stays nil when there are no notes, and one that the value survives a JSON-LD round trip. Closes #20 Co-Authored-By: Claude Opus 5 (1M context) --- Sources/Ontology/Types/Event.swift | 8 +++++- Tests/OntologyTests/EventTests.swift | 42 ++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/Sources/Ontology/Types/Event.swift b/Sources/Ontology/Types/Event.swift index fbf12ef..16434a0 100644 --- a/Sources/Ontology/Types/Event.swift +++ b/Sources/Ontology/Types/Event.swift @@ -6,6 +6,9 @@ public struct Event: Hashable, Sendable { /// The name/title of the event public var name: String? + /// Description of the event + public var description: String? + /// The calendar this event belongs to public var calendar: String? @@ -47,6 +50,7 @@ public struct Event: Hashable, Sendable { /// Initialize an Event with an EventKit event public init(_ event: EKEvent) { self.name = event.title + self.description = event.notes self.calendar = event.calendar?.title self.startDate = DateTime(event.startDate, timeZone: event.timeZone) self.endDate = DateTime(event.endDate, timeZone: event.timeZone) @@ -58,7 +62,7 @@ public struct Event: Hashable, Sendable { extension Event: Codable { private enum CodingKeys: String, CodingKey { - case name, startDate, endDate, location, url, calendar + case name, description, startDate, endDate, location, url, calendar } public func encode(to encoder: Encoder) throws { @@ -77,6 +81,7 @@ extension Event: Codable { // Encode properties try container.encodeIfPresent(name, forKey: .attribute(.name)) + try container.encodeIfPresent(description, forKey: .attribute(.description)) try container.encodeIfPresent(calendar, forKey: .attribute(.calendar)) try container.encodeIfPresent(startDate, forKey: .attribute(.startDate)) try container.encodeIfPresent(endDate, forKey: .attribute(.endDate)) @@ -103,6 +108,7 @@ extension Event: Codable { // Decode properties name = try container.decodeIfPresent(String.self, forKey: .attribute(.name)) + description = try container.decodeIfPresent(String.self, forKey: .attribute(.description)) calendar = try container.decodeIfPresent(String.self, forKey: .attribute(.calendar)) startDate = try container.decodeIfPresent(DateTime.self, forKey: .attribute(.startDate)) endDate = try container.decodeIfPresent(DateTime.self, forKey: .attribute(.endDate)) diff --git a/Tests/OntologyTests/EventTests.swift b/Tests/OntologyTests/EventTests.swift index ecc42d3..b6c25d8 100644 --- a/Tests/OntologyTests/EventTests.swift +++ b/Tests/OntologyTests/EventTests.swift @@ -65,6 +65,48 @@ struct EventTests { #expect(minimalEvent.url == nil) } + @Test("Event initialization preserves notes as description") + func testNotesPreservation() throws { + let eventStore = EKEventStore() + let event = EKEvent(eventStore: eventStore) + + event.title = "Test Event" + event.startDate = Date(timeIntervalSinceReferenceDate: 0) + event.endDate = Date(timeIntervalSinceReferenceDate: 3600) + event.notes = "Bring the signed copy" + + let ontologyEvent = Event(event) + + #expect(ontologyEvent.description == "Bring the signed copy") + + let withoutNotes = EKEvent(eventStore: eventStore) + withoutNotes.title = "Test Event" + withoutNotes.startDate = Date(timeIntervalSinceReferenceDate: 0) + withoutNotes.endDate = Date(timeIntervalSinceReferenceDate: 3600) + + #expect(Event(withoutNotes).description == nil) + } + + @Test("Event round-trip serialization preserves notes") + func testNotesRoundTrip() throws { + let eventStore = EKEventStore() + let event = EKEvent(eventStore: eventStore) + + event.title = "Test Event" + event.startDate = Date(timeIntervalSinceReferenceDate: 0) + event.endDate = Date(timeIntervalSinceReferenceDate: 3600) + event.notes = "Bring the signed copy" + + let original = Event(event) + let encoded = try JSONEncoder().encode(original) + + let json = try JSONSerialization.jsonObject(with: encoded) as! [String: Any] + #expect(json["description"] as? String == "Bring the signed copy") + + let decoded = try JSONDecoder().decode(Event.self, from: encoded) + #expect(decoded.description == original.description) + } + @Test("Event JSON-LD encoding preserves all properties") func testJSONLDEncoding() throws { let eventStore = EKEventStore()