From 3528e65312cf82f2a911e8ca132fe401ae132940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Pluta?= Date: Fri, 17 Jan 2025 17:03:50 +0100 Subject: [PATCH] Add support for '--test' CLI option --- src/backend/ntfy.ts | 7 ++++--- src/cli/index.ts | 18 +++++++++++++----- src/database/serializer.ts | 19 ++++++++++++------- src/loader/index.ts | 2 +- src/runner/index.ts | 17 ++++++++++++++++- src/types/cli.ts | 1 + src/util/{index.ts => code.ts} | 0 src/util/config.ts | 17 +++++++++++++++++ 8 files changed, 64 insertions(+), 17 deletions(-) rename src/util/{index.ts => code.ts} (100%) create mode 100644 src/util/config.ts diff --git a/src/backend/ntfy.ts b/src/backend/ntfy.ts index fcf8d0f..70aec33 100644 --- a/src/backend/ntfy.ts +++ b/src/backend/ntfy.ts @@ -3,20 +3,21 @@ import { BackendSettings } from "../types/config"; import { Notification } from "../types/notification"; import { Backend } from "./base"; import { TaskPriority } from "../types/task"; +import { enhancedStringConfig } from "../util/config"; type Config = { url: string; - tokenFile: string; + token: string; topic?: string; } & BackendSettings; export class NtfySH extends Backend { public name = "ntfy"; - protected requiredFields = ['url', 'tokenFile'] as const; + protected requiredFields = ['url', 'token'] as const; protected notify(config: Config, notification: Notification): void { - const token = fs.readFileSync(config.tokenFile, 'utf-8'); + const token = enhancedStringConfig(config.token); fetch(`https://${config.url}/${config.topic || 'obsidian'}`, { method: 'POST', diff --git a/src/cli/index.ts b/src/cli/index.ts index 8ace330..faeed85 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -2,13 +2,14 @@ import { program } from "commander"; import { CLIOptions } from "../types/cli"; import { loadConfig } from "../config"; -import { notify, scan } from "../runner"; +import { notify, scan, test } from "../runner"; const getOptions = () => program .name("obsidian-tasks-reminder") .version("0.0.1") - .requiredOption("-c, --config ", "sets the path to the YAML file with configuration") - .option("-x, --set ", "overrides the config option for this specific run (arg: =, i.e. backend.ntfy.enable=false", (v: string, prev: string[]) => prev.concat([v]), []) + .requiredOption("-c, --config ", "sets the path to the YAML file with configuration") + .option("-x, --set ", "overrides the config option for this specific run (arg: =, i.e. backend.ntfy.enable=false", (v: string, prev: string[]) => prev.concat([v]), []) + .option("-t, --test", "evaluates the query, applies the mapper and prints to stdout the notifications about to be trigger, without actual triggering them") .option("-s, --scan", "scans new tasks for future notifications and generates the database") .option("-n, --notify", "reads the generated database and triggers notifications if any") .parse() @@ -36,12 +37,19 @@ const getOptions = () => program current = current[segment]; }); } + + if (options.test) { + await test(config); + return; + } if (options.scan) { - scan(config); + await scan(config); + return; } if (options.notify) { - notify(config); + await notify(config); + return; } } diff --git a/src/database/serializer.ts b/src/database/serializer.ts index f42e5e0..2c02c42 100644 --- a/src/database/serializer.ts +++ b/src/database/serializer.ts @@ -1,7 +1,7 @@ import fs from "fs"; import { Task } from "../types/task"; import { Notification, NotificationDatabase } from "../types/notification"; -import { jsMapper } from "../util"; +import { jsMapper } from "../util/code"; /** @@ -16,19 +16,24 @@ export function dumpDatabase(file: string, tasks: Task[], mapper?: string) { * Applies the mapper for each task from list, groups them by time and serializes into JSON format. */ export function serializeDatabase(tasks: Task[], mapper?: string): string { - const transformer = mapper - ? jsMapper(mapper, {}) - : defaultMapper; + return JSON.stringify(createDatabase(tasks, mapper)); +} - const output = tasks.map(wrapWithTimeFiller(transformer)).reduce((acc, n) => { +/** + * Applies the mapper for each task from list and groups them by time. + */ +export function createDatabase(tasks: Task[], mapper?: string): NotificationDatabase { + const transformer = mapper + ? jsMapper(mapper, {}) + : defaultMapper; + + return tasks.map(wrapWithTimeFiller(transformer)).reduce((acc, n) => { if (n.time) { (acc[n.time] = (acc[n.time] || [])).push(n); }; return acc; }, {} as NotificationDatabase); - - return JSON.stringify(output); } function wrapWithTimeFiller(mapper: (task: Task) => Notification): (task: Task) => Notification { diff --git a/src/loader/index.ts b/src/loader/index.ts index d640e73..3278552 100644 --- a/src/loader/index.ts +++ b/src/loader/index.ts @@ -5,7 +5,7 @@ import { parse } from "../generated/grammar/task"; import { Task as DefaultTask } from "../model"; import { ParseResult } from "../types/grammar"; import { Task, TaskPriority } from "../types/task"; -import { jsMapper } from "../util"; +import { jsMapper } from "../util/code"; /** * Returns all tasks from specified directory and filters them with optional query. diff --git a/src/runner/index.ts b/src/runner/index.ts index 059c998..4a02a4e 100644 --- a/src/runner/index.ts +++ b/src/runner/index.ts @@ -1,9 +1,24 @@ import { loadTasks } from "../loader"; -import { dumpDatabase } from "../database/serializer"; +import { createDatabase, dumpDatabase } from "../database/serializer"; import { loadDatabase } from "../database/deserializer"; import { remind } from "../backend"; import { Config } from "../types/config"; +export async function test(config: Config) { + const tasks = await loadTasks(config.sources, config.query); + const db = createDatabase(tasks, config.mapper); + + for (const time of Object.keys(db)) { + console.log(time); + + for (const notification of db[time]) { + console.log(` - title: ${notification.title}\n text: ${notification.text}\n priority: ${notification.priority}\n tags: ${notification.tags?.join(",")}`) + } + + console.log(); + } +} + export async function scan(config: Config) { const tasks = await loadTasks(config.sources, config.query); dumpDatabase(config.databaseFile, tasks, config.mapper); diff --git a/src/types/cli.ts b/src/types/cli.ts index eb1544c..72f74c3 100644 --- a/src/types/cli.ts +++ b/src/types/cli.ts @@ -1,5 +1,6 @@ export type CLIOptions = { config: string; + test: boolean; scan: boolean; notify: boolean; set: string[]; diff --git a/src/util/index.ts b/src/util/code.ts similarity index 100% rename from src/util/index.ts rename to src/util/code.ts diff --git a/src/util/config.ts b/src/util/config.ts new file mode 100644 index 0000000..546fe49 --- /dev/null +++ b/src/util/config.ts @@ -0,0 +1,17 @@ +import { readFileSync } from "fs"; + +const specialOptions: Record string> = { + $__file: (arg: string) => readFileSync(arg, 'utf8').trim() +}; + +export const enhancedStringConfig = (value: string) => { + const trimmed = value.trim(); + + for(const opt of Object.keys(specialOptions)) { + if(trimmed.startsWith(`${opt}:`) && opt in specialOptions) { + return specialOptions[opt](trimmed.slice(opt.length + 1).trim()); + } + } + + return trimmed; +}; \ No newline at end of file