Skip to content

Commit

Permalink
Add Zod for parsing event data (OFC-1) (#331)
Browse files Browse the repository at this point in the history
  • Loading branch information
davish authored Apr 14, 2023
1 parent a58588b commit 9d8f866
Show file tree
Hide file tree
Showing 22 changed files with 872 additions and 395 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
- uses: actions/setup-node@v2
with:
node-version: "16.x"
- run: npm ci
- run: npm ci --legacy-peer-deps
- run: npm run lint
- run: npm run compile
tests:
Expand All @@ -23,5 +23,5 @@ jobs:
- uses: actions/setup-node@v2
with:
node-version: "16.x"
- run: npm ci
- run: npm ci --legacy-peer-deps
- run: npm run test
90 changes: 88 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@
"@typescript-eslint/parser": "^5.2.0",
"builtin-modules": "^3.2.0",
"esbuild": "0.13.12",
"fast-check": "^3.8.0",
"husky": "^7.0.4",
"jest": "^29.3.1",
"obsidian": "^0.16.3",
"prettier": "^2.5.1",
"ts-jest": "^29.0.3",
"tslib": "2.3.1",
"typescript": "4.4.4"
"typescript": "4.4.4",
"zod-fast-check": "^0.9.0"
},
"dependencies": {
"@fullcalendar/core": "^5.10.1",
Expand All @@ -65,6 +67,7 @@
"obsidian-daily-notes-interface": "^0.9.4",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"rrule": "^2.7.2"
"rrule": "^2.7.2",
"zod": "^3.21.4"
}
}
4 changes: 2 additions & 2 deletions src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ Following the advice in [this blog post on architecture docs](https://matklad.gi

This module defines some common types used throughout the code. The most prevalent is `OFCEvent`, short for Obsidian Full Calendar Event, that specifies the intermediate representation for all events in the plugin. Note that FullCalendar.io uses a different event format called `EventInput`, which you can read about [in their documentation](https://fullcalendar.io/docs/event-parsing).

Translation between `OFCEvent` and `EventInput` is handled in `interop.ts`. Each `Calendar` subclass (see below) handles its own translation between its source format and `OFCEvent`.
`OFCEvent` is derived from a [Zod parser](https://github.com/colinhacks/zod) that handles parsing/validating JavaScript objects into the expected shape of an event. You can check out the parser in `types/schema.ts`.

Objects can be validated as OFCEvents using `validateEvent()` . This function is used throughout the code to ensure that only valid events are present.
Translation between `OFCEvent` and `EventInput` is handled in `interop.ts`. Each `Calendar` subclass (see below) handles its own translation from its source format into `OFCEvent`.

### `core`

Expand Down
21 changes: 9 additions & 12 deletions src/calendars/DailyNoteCalendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,7 @@ import {
} from "obsidian-daily-notes-interface";
import { EventPathLocation } from "../core/EventStore";
import { ObsidianInterface } from "../ObsidianAdapter";
import {
OFCEvent,
EventLocation,
CalendarInfo,
validateEvent,
SingleEventData,
} from "../types";
import { OFCEvent, EventLocation, CalendarInfo, validateEvent } from "../types";
import { EventResponse } from "./Calendar";
import { EditableCalendar, EditableEventResponse } from "./EditableCalendar";

Expand Down Expand Up @@ -161,9 +155,12 @@ const generateInlineAttributes = (attrs: Record<string, any>): string => {
};

const makeListItem = (
data: SingleEventData,
data: OFCEvent,
whitespacePrefix: string = ""
): string => {
if (data.type !== "single") {
throw new Error("Can only pass in single event.");
}
const { completed, title } = data;
const checkbox = (() => {
if (completed !== null && completed !== undefined) {
Expand All @@ -172,13 +169,13 @@ const makeListItem = (
return null;
})();

const attrs: Partial<SingleEventData> = { ...data };
const attrs: Partial<OFCEvent> = { ...data };
delete attrs["completed"];
delete attrs["title"];
delete attrs["type"];
delete attrs["date"];

for (const key of <(keyof SingleEventData)[]>Object.keys(attrs)) {
for (const key of <(keyof OFCEvent)[]>Object.keys(attrs)) {
if (attrs[key] === undefined || attrs[key] === null) {
delete attrs[key];
}
Expand All @@ -193,7 +190,7 @@ const makeListItem = (
} ${title} ${generateInlineAttributes(attrs)}`;
};

const modifyListItem = (line: string, data: SingleEventData): string | null => {
const modifyListItem = (line: string, data: OFCEvent): string | null => {
const listMatch = line.match(listRegex);
if (!listMatch) {
console.warn(
Expand All @@ -213,7 +210,7 @@ const modifyListItem = (line: string, data: SingleEventData): string | null => {
// TODO: refactor this to not do the weird props thing
type AddToHeadingProps = {
heading: HeadingCache | undefined;
item: SingleEventData;
item: OFCEvent;
headingText: string;
};
const addToHeading = (
Expand Down
Loading

0 comments on commit 9d8f866

Please sign in to comment.