Skip to content

feat(scheduled-tasks): database query DSL trigger#9

Merged
kooksee merged 1 commit into
mainfrom
feat/scheduled-task-query-trigger
Jun 24, 2026
Merged

feat(scheduled-tasks): database query DSL trigger#9
kooksee merged 1 commit into
mainfrom
feat/scheduled-task-query-trigger

Conversation

@kooksee

@kooksee kooksee commented Jun 24, 2026

Copy link
Copy Markdown

Summary

  • Add database_query scheduled task check type that runs database.query DSL on each tick
  • Notify (in-app + desktop) when row queries match or aggregate groups return results; dedupe by result hash
  • Settings UI: trigger modes Reminder / Page filters / Query with database picker, DSL editor, and preview

Test plan

  • packages/client tests pass (scheduled-task-query-check.test.ts)
  • @colanode/ui build passes
  • Manual: packaged desktop app — Query trigger, Run now, notifications

Made with Cursor

Run database.query on schedule and notify when rows or aggregate groups
match. Settings UI adds Query trigger mode with DSL editor and preview.

Co-authored-by: Cursor <cursoragent@cursor.com>
@kooksee kooksee merged commit 04037fe into main Jun 24, 2026
@kooksee kooksee deleted the feat/scheduled-task-query-trigger branch June 24, 2026 08:46

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces support for database query triggers in scheduled tasks, allowing users to run tasks based on custom DSL queries (including aggregate queries). It adds the necessary backend execution logic, UI components for query configuration and previewing, and comprehensive unit tests. The review feedback highlights a few key improvements: ensuring the aggregate query deduplication hash includes actual row data rather than just the group count, safely handling potentially missing name attributes in matched rows, and falling back to an empty string when a database selection is cleared in the UI to prevent type mismatches.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +601 to +607
resultHash: JSON.stringify({
type: check.type,
databaseId: check.databaseId,
dsl: check.dsl,
mode: 'aggregated',
groupCount,
}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using only groupCount in the resultHash for aggregate queries means that if the aggregate values or group keys change but the total number of groups remains the same, the hash will not change. As a result, deduplication will prevent notifications from being sent even when the underlying data has changed. Including the actual result.rows in the hash ensures that any changes to the aggregated data are correctly detected.

      resultHash: JSON.stringify({
        type: check.type,
        databaseId: check.databaseId,
        dsl: check.dsl,
        mode: 'aggregated',
        rows: result.rows,
      }),

Comment on lines +633 to +637
const attributes = JSON.parse(row.attributes) as
| PageAttributes
| RecordAttributes;
matchedPageIds.push(row.id);
matchedPageNames.push(attributes.name);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If a matched row does not have a name property (or if the attributes are empty/corrupted), attributes.name will be undefined. Pushing undefined into matchedPageNames (which is typed as string[]) violates the type contract and can result in user-facing issues like displaying "1 row matched: undefined". Using optional chaining and a fallback empty string prevents this.

    const attributes = JSON.parse(row.attributes) as
      | PageAttributes
      | RecordAttributes;
    matchedPageIds.push(row.id);
    matchedPageNames.push(attributes?.name ?? '');

Comment on lines +114 to +119
onChange={(databaseId) =>
onChange({
...check,
databaseId,
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The databaseId parameter passed to onChange can be null (e.g., if the selection is cleared). Since ScheduledTaskDatabaseQueryCheck.databaseId is typed as a non-nullable string, assigning null directly violates the type definition and can lead to runtime errors. Fall back to an empty string to ensure type safety.

Suggested change
onChange={(databaseId) =>
onChange({
...check,
databaseId,
})
}
onChange={(databaseId) =>
onChange({
...check,
databaseId: databaseId ?? '',
})
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant