Skip to content

fix: ensure owner is present throughout the jobs#1816

Merged
aalemayhu merged 1 commit intomainfrom
fix/missing-owner-column
Jul 4, 2025
Merged

fix: ensure owner is present throughout the jobs#1816
aalemayhu merged 1 commit intomainfrom
fix/missing-owner-column

Conversation

@aalemayhu
Copy link
Copy Markdown
Contributor

@aalemayhu aalemayhu commented Jul 4, 2025

Fixes this report: [ERROR] [2anki.net] - Undefined binding(s) detected
when compiling SELECT. Undefined column(s): [owner] query: select * from
"jobs" where "owner" = ?

Signed-off-by: Alexander Alemayhu alexander@alemayhu.com

Summary by CodeRabbit

  • New Features

    • Introduced new use cases for job management, including job creation, cancellation, status updates, workspace setup, flashcard creation, deck building, user notification, and job completion.
    • Enhanced job repository with methods for creating, retrieving, updating, and deleting jobs.
  • Bug Fixes

    • Improved handling of empty or missing values for job titles and flashcard content to prevent errors and ensure consistent behavior.
  • Refactor

    • Replaced monolithic job management with modular use-case-driven architecture for better maintainability and clarity.
    • Removed legacy job management and notification code.
  • Style

    • Updated email notification templates with a new closing signature.
  • Chores

    • Improved input validation and error handling throughout job processing workflows.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jul 4, 2025

Walkthrough

This update removes the monolithic ConversionJob class and replaces it with a modular, use-case-driven architecture for job management and flashcard conversion. New use case classes handle specific job lifecycle steps, and a repository pattern is adopted for job data access. Several helper functions and files are refactored or removed, and email notification handling is updated.

Changes

File(s) Change Summary
src/lib/storage/jobs/ConversionJob.ts
src/lib/storage/jobs/helpers/notifyUserIfNecessary.ts
Deleted: Removed the ConversionJob class and the notifyUserIfNecessary helper function and interface.
src/lib/storage/jobs/helpers/performConversion.ts Refactored: Rewrote to use new use case classes and repository for modular job processing and error handling.
src/controllers/NotionController.ts Updated: Ensured title parameter defaults to 'Untitled' if not provided.
src/data_layer/JobRepository.ts Enhanced: Added new CRUD methods, switched to object_id key, and improved job data access patterns.
src/services/NotionService/BlockHandler/BlockHandler.ts Fixed: Ensured ankiNote.back is always a string and conditionally preserved newlines.
src/services/NotionService/helpers/preserveNewlinesIfApplicable.ts Improved: Allowed nullable/undefined input and added guard clause for empty values.
src/services/NotionService/blocks/media/BlockBookmark/hooks/useMetadata.ts Fixed: Removed invocation of required metascraper modules.
src/services/EmailService/templates/convert-link.html Updated: Changed email signature to a team-based, branded closing.
src/usecases/jobs/BuildDeckForJobUseCase.ts
.../CancelJobUseCase.ts
.../CheckInProgressJobUseCase.ts
.../CheckJobLimitUseCase.ts
.../CompleteJobUseCase.ts
.../CreateFlashcardsForJobUseCase.ts
.../CreateJobUseCase.tsx
.../CreateJobWorkSpaceUseCase.ts
.../FindOrCreateJobUseCase.ts
.../NotifyUserUseCase.ts
.../SetJobFailedUseCase.ts
.../StartJobUseCase.ts
Added: Introduced new use case classes for modular job management, workspace creation, flashcard generation, deck building, notifications, and error handling.

Sequence Diagram(s)

sequenceDiagram
    participant Controller
    participant JobRepository
    participant FindOrCreateJob
    participant CheckInProgressJob
    participant CheckJobLimit
    participant CancelJob
    participant StartJob
    participant CreateWorkspace
    participant CreateFlashcards
    participant BuildDeck
    participant NotifyUser
    participant SetJobFailed
    participant CompleteJob

    Controller->>FindOrCreateJob: execute({id, owner, title, type})
    FindOrCreateJob->>JobRepository: findJobById(id, owner)
    alt Job exists
        FindOrCreateJob-->>Controller: return job
    else
        FindOrCreateJob->>JobRepository: create(id, owner, title, type)
        FindOrCreateJob->>JobRepository: findJobById(id, owner)
        FindOrCreateJob-->>Controller: return job
    end
    Controller->>CheckInProgressJob: execute(id, owner)
    alt In progress or failed
        Controller-->>Client: return (job already in progress/failed)
    else
        Controller->>CheckJobLimit: execute({owner, maxJobs})
        alt Limit exceeded
            Controller->>CancelJob: execute({id, owner, reason})
            Controller-->>Client: return (job cancelled)
        else
            Controller->>StartJob: execute({id, owner})
            Controller->>CreateWorkspace: execute({id, owner, api, jobRepository})
            Controller->>CreateFlashcards: execute({bl, id, owner, rules, settings, type})
            Controller->>BuildDeck: execute({bl, exporter, decks, ws, settings, storage, id, owner})
            Controller->>NotifyUser: execute({owner, rules, db, key, id, size, apkg})
            Controller->>CompleteJob: execute(id, owner)
            Controller-->>Client: return (job started)
        end
    end
    Note over Controller,Client: On error, SetJobFailed is called and error returned to client if waiting
Loading

Poem

A bunny hopped through code so wide,
Splitting jobs with modular pride.
No longer one big monolith,
Each task now has its own myth.
With emails sent and decks anew,
This hop brings structure—cheerful too!
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error code ERESOLVE
npm error ERESOLVE could not resolve
npm error
npm error While resolving: @typescript-eslint/eslint-plugin@8.19.1
npm error Found: eslint@8.56.0
npm error node_modules/eslint
npm error dev eslint@"8.56.0" from the root project
npm error peer eslint@"^6.0.0 || ^7.0.0 || >=8.0.0" from @eslint-community/eslint-utils@4.4.0
npm error node_modules/@eslint-community/eslint-utils
npm error @eslint-community/eslint-utils@"^4.4.0" from @typescript-eslint/utils@8.19.1
npm error node_modules/@typescript-eslint/utils
npm error @typescript-eslint/utils@"8.19.1" from @typescript-eslint/eslint-plugin@8.19.1
npm error node_modules/@typescript-eslint/eslint-plugin
npm error dev @typescript-eslint/eslint-plugin@"^8.9.0" from the root project
npm error 1 more (@typescript-eslint/type-utils)
npm error @eslint-community/eslint-utils@"^4.2.0" from eslint@8.56.0
npm error 5 more (eslint-config-airbnb-base, ...)
npm error
npm error Could not resolve dependency:
npm error peer eslint@"^8.57.0 || ^9.0.0" from @typescript-eslint/eslint-plugin@8.19.1
npm error node_modules/@typescript-eslint/eslint-plugin
npm error dev @typescript-eslint/eslint-plugin@"^8.9.0" from the root project
npm error
npm error Conflicting peer dependency: eslint@9.30.1
npm error node_modules/eslint
npm error peer eslint@"^8.57.0 || ^9.0.0" from @typescript-eslint/eslint-plugin@8.19.1
npm error node_modules/@typescript-eslint/eslint-plugin
npm error dev @typescript-eslint/eslint-plugin@"^8.9.0" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error /.npm/_logs/2025-07-04T14_05_26_691Z-eresolve-report.txt
npm error A complete log of this run can be found in: /.npm/_logs/2025-07-04T14_05_26_691Z-debug-0.log

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@aalemayhu aalemayhu force-pushed the fix/missing-owner-column branch from d8dd471 to f4a5da7 Compare July 4, 2025 14:02
Fixes this report: [ERROR] [2anki.net] - Undefined binding(s) detected
when compiling SELECT. Undefined column(s): [owner] query: select * from
"jobs" where "owner" = ?

Signed-off-by: Alexander Alemayhu <alexander@alemayhu.com>
@aalemayhu aalemayhu force-pushed the fix/missing-owner-column branch from f4a5da7 to f2143d3 Compare July 4, 2025 14:04
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Jul 4, 2025

@aalemayhu aalemayhu merged commit 4361ada into main Jul 4, 2025
5 of 6 checks passed
@aalemayhu aalemayhu deleted the fix/missing-owner-column branch July 4, 2025 14:08
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🧹 Nitpick comments (3)
src/usecases/jobs/CreateJobUseCase.tsx (1)

1-22: Fix file extension and clarify return type assumption.

  1. The file extension should be .ts instead of .tsx since this contains TypeScript without JSX.
  2. The method assumes jobRepository.create() returns an array and takes the first element as a number, but this assumption should be verified or documented.
-// File: src/usecases/jobs/CreateJobUseCase.tsx
+// File: src/usecases/jobs/CreateJobUseCase.ts

Consider adding a comment explaining what result[0] represents or ensure the repository method's return type contract is well-defined.

src/usecases/jobs/CreateFlashcardsForJobUseCase.ts (1)

32-38: Add explanation for ID dash removal.

The code removes dashes from the ID with id.replace(/-/g, '') but there's no comment explaining why this transformation is necessary.

return bl.findFlashcards({
  parentType: type ?? 'page',
+  // Remove dashes to match expected ID format for findFlashcards
  topLevelId: id.replace(/-/g, ''),
  rules,
  decks: [],
  parentName: settings.deckName || '',
});
src/usecases/jobs/BuildDeckForJobUseCase.ts (1)

50-50: Remove unnecessary type assertion.

The type assertion as string appears unnecessary and could mask type issues. If gen.run() doesn't return the expected type, it's better to fix the return type or handle it explicitly.

-    const payload = (await gen.run()) as string;
+    const payload = await gen.run();
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3ce17a and f2143d3.

📒 Files selected for processing (21)
  • src/controllers/NotionController.ts (1 hunks)
  • src/data_layer/JobRepository.ts (2 hunks)
  • src/lib/storage/jobs/ConversionJob.ts (0 hunks)
  • src/lib/storage/jobs/helpers/notifyUserIfNecessary.ts (0 hunks)
  • src/lib/storage/jobs/helpers/performConversion.ts (3 hunks)
  • src/services/EmailService/templates/convert-link.html (2 hunks)
  • src/services/NotionService/BlockHandler/BlockHandler.ts (2 hunks)
  • src/services/NotionService/blocks/media/BlockBookmark/hooks/useMetadata.ts (1 hunks)
  • src/services/NotionService/helpers/preserveNewlinesIfApplicable.ts (1 hunks)
  • src/usecases/jobs/BuildDeckForJobUseCase.ts (1 hunks)
  • src/usecases/jobs/CancelJobUseCase.ts (1 hunks)
  • src/usecases/jobs/CheckInProgressJobUseCase.ts (1 hunks)
  • src/usecases/jobs/CheckJobLimitUseCase.ts (1 hunks)
  • src/usecases/jobs/CompleteJobUseCase.ts (1 hunks)
  • src/usecases/jobs/CreateFlashcardsForJobUseCase.ts (1 hunks)
  • src/usecases/jobs/CreateJobUseCase.tsx (1 hunks)
  • src/usecases/jobs/CreateJobWorkSpaceUseCase.ts (1 hunks)
  • src/usecases/jobs/FindOrCreateJobUseCase.ts (1 hunks)
  • src/usecases/jobs/NotifyUserUseCase.ts (1 hunks)
  • src/usecases/jobs/SetJobFailedUseCase.ts (1 hunks)
  • src/usecases/jobs/StartJobUseCase.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • src/lib/storage/jobs/helpers/notifyUserIfNecessary.ts
  • src/lib/storage/jobs/ConversionJob.ts
🧰 Additional context used
🧬 Code Graph Analysis (7)
src/usecases/jobs/NotifyUserUseCase.ts (2)
src/data_layer/public/ParserRules.ts (1)
  • ParserRules (8-24)
src/services/EmailService/EmailService.ts (1)
  • useDefaultEmailService (405-410)
src/usecases/jobs/CancelJobUseCase.ts (1)
src/data_layer/public/Jobs.ts (1)
  • Jobs (8-26)
src/lib/storage/jobs/helpers/performConversion.ts (12)
src/usecases/jobs/FindOrCreateJobUseCase.ts (1)
  • FindOrCreateJobUseCase (12-38)
src/usecases/jobs/CheckInProgressJobUseCase.ts (1)
  • CheckInProgressJobUseCase (3-18)
src/usecases/jobs/CheckJobLimitUseCase.ts (1)
  • CheckJobLimitUseCase (8-18)
src/lib/isPaying.ts (1)
  • isPaying (1-6)
src/usecases/jobs/CancelJobUseCase.ts (1)
  • CancelJobUseCase (10-27)
src/usecases/jobs/StartJobUseCase.ts (1)
  • StartJobUseCase (9-26)
src/usecases/jobs/CreateJobWorkSpaceUseCase.ts (1)
  • CreateJobWorkSpaceUseCase (26-60)
src/usecases/jobs/CreateFlashcardsForJobUseCase.ts (1)
  • CreateFlashcardsForJobUseCase (16-40)
src/usecases/jobs/SetJobFailedUseCase.ts (1)
  • SetJobFailedUseCase (3-19)
src/usecases/jobs/BuildDeckForJobUseCase.ts (1)
  • BuildDeckForJobUseCase (36-74)
src/usecases/jobs/NotifyUserUseCase.ts (1)
  • NotifyUserUseCase (17-38)
src/usecases/jobs/CompleteJobUseCase.ts (1)
  • CompleteJobUseCase (3-19)
src/usecases/jobs/StartJobUseCase.ts (1)
src/data_layer/public/Jobs.ts (1)
  • Jobs (8-26)
src/usecases/jobs/FindOrCreateJobUseCase.ts (2)
src/data_layer/public/Jobs.ts (1)
  • Jobs (8-26)
src/usecases/jobs/CreateJobUseCase.tsx (1)
  • CreateJobUseCase (10-21)
src/usecases/jobs/BuildDeckForJobUseCase.ts (3)
src/lib/parser/Deck.ts (1)
  • Deck (4-53)
src/lib/anki/format.ts (3)
  • isValidDeckName (4-5)
  • addDeckNameSuffix (7-8)
  • DECK_NAME_SUFFIX (1-1)
src/lib/misc/file.ts (1)
  • FileSizeInMegaBytes (23-26)
src/data_layer/JobRepository.ts (1)
src/data_layer/public/Jobs.ts (1)
  • Jobs (8-26)
🔇 Additional comments (17)
src/services/EmailService/templates/convert-link.html (1)

28-30: LGTM! Professional branding improvement.

The signature change from individual developer to team branding is a good improvement for professional presentation.

src/controllers/NotionController.ts (1)

94-94: LGTM! Good defensive programming practice.

The null coalescing operator ensures title is always a string, preventing potential downstream errors when the title is null or undefined.

src/services/NotionService/BlockHandler/BlockHandler.ts (2)

200-200: LGTM! Consistent fallback handling.

Using || operator provides a consistent fallback to empty string for falsy values.


218-223: LGTM! Safer conditional processing.

Adding the conditional check before calling preserveNewlinesIfApplicable prevents potential errors when ankiNote.back is falsy and improves robustness.

src/usecases/jobs/CheckInProgressJobUseCase.ts (1)

6-17: Owner parameter correctly included, but review the "in progress" logic.

The use case correctly includes the owner parameter in the job query (findJobById(id, owner)), which directly addresses the PR objective of ensuring owner is present throughout job operations.

However, the logic for determining "in progress" status seems questionable:

  • Line 12-14: Returns true for 'started' status ✓
  • Line 16: Returns true for 'failed' status ❓

Why would a 'failed' job be considered "in progress"? This seems counterintuitive - failed jobs are typically terminal states, not in-progress states.

Consider clarifying the intended behavior or renaming the method to better reflect what it actually checks.

src/services/NotionService/helpers/preserveNewlinesIfApplicable.ts (1)

4-9: Good null safety improvement!

The updated function signature and guard clause properly handle nullable inputs, preventing potential runtime errors when text is null or undefined.

src/usecases/jobs/CheckJobLimitUseCase.ts (1)

8-18: Clean implementation with proper owner usage!

The use case correctly uses the owner parameter in repository calls, aligning with the PR objective of ensuring owner is present throughout job operations.

src/usecases/jobs/SetJobFailedUseCase.ts (1)

6-18: Solid implementation with proper error handling!

The use case correctly uses both id and owner parameters for job retrieval, includes appropriate error handling, and has an optimization for already failed jobs.

src/usecases/jobs/CompleteJobUseCase.ts (1)

7-17: Good owner parameter usage!

The use case correctly uses both jobId and owner parameters for job retrieval, aligning with the PR objective of ensuring owner is present throughout job operations.

src/usecases/jobs/CancelJobUseCase.ts (1)

10-26: Excellent implementation with consistent owner usage!

The use case properly uses both id and owner parameters for job operations, includes appropriate error handling, and has optimization for already cancelled jobs. This aligns perfectly with the PR objective of ensuring owner is present throughout job operations.

src/usecases/jobs/StartJobUseCase.ts (1)

4-26: LGTM! Proper owner field usage throughout.

The implementation correctly uses the owner field in both findJobById and updateJobStatus calls, which directly addresses the PR objective of ensuring owner is present throughout job operations. The error handling and cancelled job logic are also appropriate.

src/usecases/jobs/NotifyUserUseCase.ts (1)

2-2: No import path mismatch – ParserRules class is correctly imported

The code in NotifyUserUseCase.ts imports the ParserRules class from src/lib/parser/ParserRules, which is exactly where the class is defined. The interface in src/data_layer/public/ParserRules.ts isn’t used at that import site, so the concerns about differing paths and property‐name casing do not apply here. Please disregard this comment.

Likely an incorrect or invalid review comment.

src/usecases/jobs/CreateFlashcardsForJobUseCase.ts (1)

19-40: LGTM! Proper owner field usage.

The use case correctly uses the owner field in the updateJobStatus call, which aligns with the PR objective of ensuring owner is present throughout job operations.

src/usecases/jobs/BuildDeckForJobUseCase.ts (1)

36-74: Implementation correctly ensures owner presence throughout job operations.

The use case properly includes the owner parameter in storage operations (line 62) and database insertions (line 67), which aligns with the PR objective of fixing the "owner is undefined" error.

src/data_layer/JobRepository.ts (1)

13-53: Repository methods correctly enforce owner presence in all queries.

All the repository methods properly include the owner parameter in their database queries:

  • deleteJob (lines 15-16)
  • create (line 25)
  • findJobById (line 33)
  • updateJobStatus (line 45)

This ensures that job operations are properly scoped to the owner and prevents the "owner is undefined" error mentioned in the PR objectives.

src/usecases/jobs/CreateJobWorkSpaceUseCase.ts (1)

26-60: Use case correctly includes owner in job operations.

The implementation properly passes the owner parameter when updating job status (line 34) and loading settings (line 50), ensuring owner presence throughout the job workspace creation process.

src/lib/storage/jobs/helpers/performConversion.ts (1)

29-147: Refactoring successfully ensures owner presence throughout job operations.

The conversion to a use-case architecture properly addresses the PR objective. All job operations now consistently include the owner parameter:

  • Job creation (line 45)
  • Status checks (line 51)
  • Job limits (line 60)
  • Job cancellation (line 66)
  • All subsequent operations through completion

This architectural change prevents the "owner is undefined" database query error mentioned in the PR objectives.

Comment on lines +4 to +8
require('metascraper-description'),
require('metascraper-image'),
require('metascraper-logo-favicon'),
require('metascraper-title'),
require('metascraper-url'),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify metascraper API compatibility.

The change from requiring and immediately invoking metascraper modules to just requiring them suggests an API change. Ensure this aligns with the metascraper usage pattern and doesn't break functionality.


🏁 Script executed:

#!/bin/bash
# Description: Check metascraper usage in the codebase to ensure compatibility
# Expected: Find where metascraper is used and verify the pattern is consistent

# Search for metascraper usage patterns
rg -A 3 -B 3 "metascraper\(" 

# Check if there are any other require patterns for metascraper
rg "require.*metascraper"

Length of output: 1489


🌐 Web query:

Metascraper plugin modules invocation: do we call require('metascraper-description')() or just require('metascraper-description') in latest version?

💡 Result:

In the latest version of metascraper-description, you should invoke the module as a function by appending () after requiring it. This pattern is consistent across all metascraper-* modules. Here's how you can use it:

const metascraper = require('metascraper')([
  require('metascraper-description')(),
  // ... other rules
]);

This approach is detailed in the metascraper README. (github.com)

By invoking require('metascraper-description')() in this manner, you ensure that the module is properly initialized and ready for use within your metascraper setup.

Citations:


Re-add invocation parentheses for metascraper plugins

The latest metascraper-* modules export a factory function, so each rule must be invoked with (). Without the invocation, you’re passing the factory itself rather than the initialized rule, which will break at runtime.

Files to update:

  • src/services/NotionService/blocks/media/BlockBookmark/hooks/useMetadata.ts (lines 4–8)

Required diff:

-  require('metascraper-description'),
-  require('metascraper-image'),
-  require('metascraper-logo-favicon'),
-  require('metascraper-title'),
-  require('metascraper-url'),
+  require('metascraper-description')(),
+  require('metascraper-image')(),
+  require('metascraper-logo-favicon')(),
+  require('metascraper-title')(),
+  require('metascraper-url')(),

This change aligns with the metascraper README usage pattern.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
require('metascraper-description'),
require('metascraper-image'),
require('metascraper-logo-favicon'),
require('metascraper-title'),
require('metascraper-url'),
require('metascraper-description')(),
require('metascraper-image')(),
require('metascraper-logo-favicon')(),
require('metascraper-title')(),
require('metascraper-url')(),
🤖 Prompt for AI Agents
In src/services/NotionService/blocks/media/BlockBookmark/hooks/useMetadata.ts
between lines 4 and 8, the metascraper plugins are imported without invoking
their factory functions. To fix this, add parentheses after each require
statement to call the factory function, e.g., change
require('metascraper-description') to require('metascraper-description')(). This
ensures each plugin is properly initialized as required by the latest
metascraper versions.

export class CompleteJobUseCase {
constructor(private readonly jobRepository: JobRepository) {}

async execute(jobId: string, owner: string): Promise<number> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix return type mismatch!

The method signature declares Promise<number> but the implementation returns different types.

Apply this diff to fix the type consistency:

-  async execute(jobId: string, owner: string): Promise<number> {
+  async execute(jobId: string, owner: string): Promise<Jobs | number> {

Or if you intend to always return a number, update line 14:

-    return job;
+    return 0; // or appropriate number indicating cancelled job
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async execute(jobId: string, owner: string): Promise<number> {
async execute(jobId: string, owner: string): Promise<Jobs | number> {
🤖 Prompt for AI Agents
In src/usecases/jobs/CompleteJobUseCase.ts at line 6, the execute method's
return type is declared as Promise<number> but the implementation returns
different types. To fix this, ensure that all return statements inside the
method return a number wrapped in a Promise. If the method should always return
a number, update the return statements accordingly to maintain type consistency
with the declared Promise<number> return type.

Comment on lines +5 to +10
interface FindOrCreateJobUseCaseInput {
id: string;
owner: string;
title: string;
type: string;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Interface inconsistency with CreateJobUseCase.

The title and type fields are required in FindOrCreateJobUseCaseInput but optional in CreateJobUseCaseIn. This inconsistency could lead to runtime issues.

interface FindOrCreateJobUseCaseInput {
  id: string;
  owner: string;
-  title: string;
-  type: string;
+  title?: string | null;
+  type?: string;
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
interface FindOrCreateJobUseCaseInput {
id: string;
owner: string;
title: string;
type: string;
}
interface FindOrCreateJobUseCaseInput {
id: string;
owner: string;
title?: string | null;
type?: string;
}
🤖 Prompt for AI Agents
In src/usecases/jobs/FindOrCreateJobUseCase.ts between lines 5 and 10, the
interface FindOrCreateJobUseCaseInput declares the fields title and type as
required, while in CreateJobUseCaseInput these fields are optional. To fix this
inconsistency and avoid runtime issues, update the FindOrCreateJobUseCaseInput
interface to make the title and type fields optional by adding a question mark
(?) after their names.

Comment on lines +24 to +36
const createJob = new CreateJobUseCase(this.jobRepository);
await createJob.execute({
id,
owner,
title,
type,
});

const secondLookup = await this.jobRepository.findJobById(id, owner);
if (!secondLookup) {
throw new Error('Failed to find or create job after creation attempt');
}
return secondLookup;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider refactoring dependency injection and second lookup.

  1. Dependency Injection: The CreateJobUseCase is instantiated inside the method, which makes testing difficult and violates dependency inversion.

  2. Unnecessary Second Lookup: The second lookup after job creation seems redundant if the create operation could return the created job entity.

export class FindOrCreateJobUseCase {
-  constructor(private readonly jobRepository: JobRepository) {}
+  constructor(
+    private readonly jobRepository: JobRepository,
+    private readonly createJobUseCase: CreateJobUseCase
+  ) {}

  async execute(input: FindOrCreateJobUseCaseInput): Promise<Jobs> {
    const { id, owner, title, type } = input;
    
    const existingJob = await this.jobRepository.findJobById(id, owner);
    if (existingJob) {
      return existingJob;
    }

-    const createJob = new CreateJobUseCase(this.jobRepository);
-    await createJob.execute({
+    const createdJobId = await this.createJobUseCase.execute({
      id,
      owner,
      title,
      type,
    });

-    const secondLookup = await this.jobRepository.findJobById(id, owner);
-    if (!secondLookup) {
-      throw new Error('Failed to find or create job after creation attempt');
-    }
-    return secondLookup;
+    // If create returns the job entity instead of ID, this could be simplified
+    return await this.jobRepository.findJobById(id, owner);
  }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/usecases/jobs/FindOrCreateJobUseCase.ts around lines 24 to 36, refactor
the code to inject CreateJobUseCase via the constructor or as a class dependency
instead of instantiating it inside the method to improve testability and adhere
to dependency inversion. Also, modify the createJob.execute method to return the
created job entity directly, eliminating the need for the redundant second
lookup from the repository after creation.

async execute(input: NotifyUserUseCaseInput): Promise<void> {
const { owner, rules, db, key, id, size, apkg } = input;

console.debug('rules.email', rules.EMAIL_NOTIFICATION);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Property access mismatch with interface definition.

The code accesses rules.EMAIL_NOTIFICATION but the ParserRules interface defines the property as email_notification (lowercase). This will cause a runtime error.

-console.debug('rules.email', rules.EMAIL_NOTIFICATION);
+console.debug('rules.email', rules.email_notification);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.debug('rules.email', rules.EMAIL_NOTIFICATION);
console.debug('rules.email', rules.email_notification);
🤖 Prompt for AI Agents
In src/usecases/jobs/NotifyUserUseCase.ts at line 23, the code accesses the
property rules.EMAIL_NOTIFICATION, but the ParserRules interface defines this
property as email_notification in lowercase. To fix this, change the property
access to rules.email_notification to match the interface definition and avoid
runtime errors.

Comment on lines +21 to +30
const updateJobStatus = await this.jobRepository.updateJobStatus(
id,
owner,
'step2_creating_flashcards',
''
);

if (!updateJobStatus) {
throw new Error('Failed to update job status');
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Clarify return type assumption for status update check.

The code checks if (!updateJobStatus) but it's unclear what the updateJobStatus method returns. This assumption should be verified or documented.


🏁 Script executed:

#!/bin/bash
# Check JobRepository.updateJobStatus method signature and return type
ast-grep --pattern 'updateJobStatus($$$) { $$$ }'
rg -A 5 "updateJobStatus.*:" src/data_layer/JobRepository.ts

Length of output: 114


🏁 Script executed:

#!/bin/bash
set -e
# Search for all occurrences and definitions of updateJobStatus in TypeScript files
rg -n "updateJobStatus" -t ts

Length of output: 779


🏁 Script executed:

#!/bin/bash
set -e
# Show the signature and implementation of updateJobStatus in JobRepository.ts
rg -n -A10 -B2 "async updateJobStatus" src/data_layer/JobRepository.ts

Length of output: 416


#!/bin/bash
set -e
# Display full implementation of updateJobStatus to confirm its return behavior
rg -n -A20 -B2 "async updateJobStatus" src/data_layer/JobRepository.ts

🏁 Script executed:

#!/bin/bash
set -e
# Show extended implementation of updateJobStatus to verify its return behavior
rg -n -A50 -B0 "async updateJobStatus" src/data_layer/JobRepository.ts

Length of output: 564


Clarify updateJobStatus return type and error handling

The updateJobStatus method actually returns a Jobs object (or undefined if no row was updated), not a boolean. To make this clear and avoid confusion, rename the variable and use an explicit null/undefined check:

• File: src/usecases/jobs/CreateFlashcardsForJobUseCase.ts
Lines: ~21–30

Suggested diff:

-   const updateJobStatus = await this.jobRepository.updateJobStatus(
+   const updatedJob = await this.jobRepository.updateJobStatus(
      id,
      owner,
      'step2_creating_flashcards',
      ''
-   );
-
-   if (!updateJobStatus) {
-     throw new Error('Failed to update job status');
-   }
+   );
+
+   if (updatedJob == null) {
+     throw new Error(`Failed to update job status for job ${id}`);
+   }

This makes it clear you’re working with the returned job record and explicitly handling the “no row updated” case.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const updateJobStatus = await this.jobRepository.updateJobStatus(
id,
owner,
'step2_creating_flashcards',
''
);
if (!updateJobStatus) {
throw new Error('Failed to update job status');
}
const updatedJob = await this.jobRepository.updateJobStatus(
id,
owner,
'step2_creating_flashcards',
''
);
if (updatedJob == null) {
throw new Error(`Failed to update job status for job ${id}`);
}
🤖 Prompt for AI Agents
In src/usecases/jobs/CreateFlashcardsForJobUseCase.ts around lines 21 to 30, the
variable updateJobStatus is misleading because the method returns a Jobs object
or undefined, not a boolean. Rename updateJobStatus to something like updatedJob
to reflect it holds the returned job record, then replace the boolean check with
an explicit null or undefined check (e.g., if (!updatedJob)) to properly handle
the case where no row was updated and throw the error accordingly.

interface BuildDeckForJobUseCaseOutput {
size: number;
key: string;
apkg: Buffer<ArrayBufferLike>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix the Buffer type declaration.

The Buffer type in TypeScript doesn't accept generic parameters. It should be declared as just Buffer.

Apply this diff to fix the type:

-  apkg: Buffer<ArrayBufferLike>;
+  apkg: Buffer;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
apkg: Buffer<ArrayBufferLike>;
apkg: Buffer;
🤖 Prompt for AI Agents
In src/usecases/jobs/BuildDeckForJobUseCase.ts at line 22, the type declaration
for apkg incorrectly uses a generic parameter with Buffer. Remove the generic
parameter and declare apkg simply as type Buffer.

Comment on lines +11 to +16
export interface CreateJobWorkSpaceUseCaseInput {
id: string;
owner: string;
api: NotionAPIWrapper;
jobRepository: JobRepository;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove redundant jobRepository from input interface.

The jobRepository is already injected via the constructor and shouldn't be part of the input interface. This redundancy could lead to confusion and maintenance issues.

 export interface CreateJobWorkSpaceUseCaseInput {
   id: string;
   owner: string;
   api: NotionAPIWrapper;
-  jobRepository: JobRepository;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface CreateJobWorkSpaceUseCaseInput {
id: string;
owner: string;
api: NotionAPIWrapper;
jobRepository: JobRepository;
}
export interface CreateJobWorkSpaceUseCaseInput {
id: string;
owner: string;
api: NotionAPIWrapper;
}
🤖 Prompt for AI Agents
In src/usecases/jobs/CreateJobWorkSpaceUseCase.ts between lines 11 and 16,
remove the jobRepository property from the CreateJobWorkSpaceUseCaseInput
interface since jobRepository is already injected through the constructor. This
will eliminate redundancy and prevent confusion by keeping jobRepository
management consistent and centralized.

Comment on lines +86 to +88
const { ws, exporter, settings, bl, rules } = await createWorkSpace.execute(
{ api, id, owner, jobRepository }
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove redundant jobRepository parameter.

The jobRepository is already injected into CreateJobWorkSpaceUseCase via its constructor and shouldn't be passed in the execute parameters.

     const { ws, exporter, settings, bl, rules } = await createWorkSpace.execute(
-      { api, id, owner, jobRepository }
+      { api, id, owner }
     );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { ws, exporter, settings, bl, rules } = await createWorkSpace.execute(
{ api, id, owner, jobRepository }
);
const { ws, exporter, settings, bl, rules } = await createWorkSpace.execute(
{ api, id, owner }
);
🤖 Prompt for AI Agents
In src/lib/storage/jobs/helpers/performConversion.ts around lines 86 to 88,
remove the jobRepository parameter from the execute call on createWorkSpace
since jobRepository is already injected via the constructor of
CreateJobWorkSpaceUseCase. Update the execute call to only pass { api, id, owner
} without jobRepository.

Comment on lines +52 to 54
if (!hasInProgressJob) {
console.log(`job ${id} was not started. Job is already active.`);
return res ? res.redirect('/uploads') : null;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect log message.

The log message is backwards - it says "was not started" when hasInProgressJob is false, but the condition suggests the job is already active.

-      console.log(`job ${id} was not started. Job is already active.`);
+      console.log(`job ${id} is already active or completed.`);
🤖 Prompt for AI Agents
In src/lib/storage/jobs/helpers/performConversion.ts around lines 52 to 54, the
log message incorrectly states "job was not started" when hasInProgressJob is
false, but this condition means the job is already active. Update the log
message to correctly reflect that the job is already active or running, ensuring
the message matches the condition's meaning.

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