Skip to content

🛠️ DX: Support full URLs and slugs as input for API methods#187

Open
bartholomej wants to merge 1 commit intomasterfrom
dx/extract-id-support-5973108370472887574
Open

🛠️ DX: Support full URLs and slugs as input for API methods#187
bartholomej wants to merge 1 commit intomasterfrom
dx/extract-id-support-5973108370472887574

Conversation

@bartholomej
Copy link
Copy Markdown
Owner

@bartholomej bartholomej commented May 7, 2026

💡 What
We've introduced a new helper function, extractId, and updated the library's primary API methods (movie, creator, userRatings, userReviews) to natively support full CSFD URLs and URL slugs as input arguments, in addition to numeric IDs.

🎯 Why
This drastically improves the Developer Experience (DX). Developers building tools on top of this library no longer need to write boilerplate code or regular expressions to extract numeric IDs from links before passing them into the API. They can just copy-paste a URL or a slug directly into the function call, and the library will figure it out automatically.

🚀 Examples
Before:

const id = parseIdFromUrl("https://www.csfd.cz/uzivatel/912-bart/hodnoceni/");
const ratings = await csfd.userRatings(id);

After:

const ratings = await csfd.userRatings("https://www.csfd.cz/uzivatel/912-bart/hodnoceni/");
const creator = await csfd.creator("3112-lilly-wachowski");

PR created automatically by Jules for task 5973108370472887574 started by @bartholomej

Summary by CodeRabbit

  • New Features

    • Movie and creator lookup methods now accept string inputs (URLs and slug formats) in addition to numeric IDs.
    • Enhanced user ID handling for ratings and reviews queries with improved input normalization.
  • Improvements

    • Refined input validation and flexibility across the API.

Add `extractId` helper function to handle varied ID formats.
Update API typings and signatures in `src/index.ts` to accept `number | string`.
Refactor `movie`, `creator`, `userRatings`, and `userReviews` services to leverage `extractId`.
Add corresponding unit tests to `tests/helpers.test.ts`.

Co-authored-by: bartholomej <5861310+bartholomej@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces flexible ID input support across the CSFD API. A new extractId() helper normalizes movie/creator/user identifiers from numbers, numeric strings, slugs, and URLs into canonical integer IDs. The public API methods and services are updated to accept number | string instead of only numbers, delegating ID normalization to this helper. Supporting code—URL generators, CLI output, and tests—is refactored for clarity without altering functionality.

Changes

Flexible ID Input Support & Code Formatting Cleanup

Layer / File(s) Summary
New ID Extraction Helper
src/helpers/global.helper.ts
Exports extractId(value: number | string): number | null that normalizes movie/creator/user IDs from multiple input forms, including integers, numeric strings, slug-formatted strings (<digits>-...), and URLs, with language-prefix-aware fallback logic.
Public API Type Updates
src/index.ts
Csfd.movie() and Csfd.creator() method signatures now accept number | string instead of only number; internal calls to services pass the raw input without numeric coercion.
Service Implementation
src/services/movie.service.ts, src/services/creator.service.ts, src/services/user-ratings.service.ts, src/services/user-reviews.service.ts
All services import and use extractId() to normalize user/movie/creator input identifiers; validation errors are thrown when extraction returns null; pagination and URL construction use normalized IDs instead of raw input.
URL Generator Updates
src/vars.ts
userRatingsUrl() and userReviewsUrl() are reformatted to multi-line arrow functions with optional page parameter; cinemasUrl() and searchUrl() are also reformatted for consistency; minor punctuation adjustment to LANGUAGE_DOMAIN_MAP for the sk key.
CLI Output & Helper Formatting
src/bin/export-reviews.ts, src/bin/lookup-movie.ts, src/bin/search.ts, src/bin/utils.ts, src/helpers/search-user.helper.ts, src/dto/options.ts, src/types.ts
CLI functions refactored with multi-line conditionals and array construction for readability; color helper alignment cleaned up; import quote style normalized to single quotes; helper filter logic reformatted without behavior changes.
Tests
tests/helpers.test.ts, test-dx-replace.ts
New Extract ID test suite covers numeric inputs, numeric strings, slugs, full URLs, null/undefined, and invalid cases; test imports updated to include extractId; placeholder test file added.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • bartholomej/node-csfd-api#137: Modifies the same CLI and utilities files, including user reviews/ratings features and variable configuration changes.
  • bartholomej/node-csfd-api#67: Adds the full UserReviewsScraper and related URL helpers that this PR refactors with flexible ID input support.
  • bartholomej/node-csfd-api#117: Modifies the same helper module (src/helpers/global.helper.ts) and touches user-ratings/user-reviews parsing logic.

Suggested labels

enhancement

Poem

🐰 With flexible IDs now in hand,
Strings and slugs join numbers, grand!
The API bends to every form—
URLs, slugs, integers—all conform.
Extract and normalize with ease,
Code reads cleaner, bound to please! 🎯

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description provides clear motivation, concrete before/after examples, and explains the new feature well. However, the required template sections (Type of change, Related Issues, Checklist) are missing. Add the missing template sections: check the appropriate 'Type of change' box, include 'Closes' information for related issues, and complete the checklist items.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding support for full URLs and slugs as input to API methods, improving DX.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dx/extract-id-support-5973108370472887574

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov-commenter
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.90909% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.36%. Comparing base (a1c5c20) to head (c21de1b).

Files with missing lines Patch % Lines
src/helpers/global.helper.ts 80.00% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #187      +/-   ##
==========================================
- Coverage   98.83%   98.36%   -0.47%     
==========================================
  Files          34       34              
  Lines         771      794      +23     
  Branches      196      208      +12     
==========================================
+ Hits          762      781      +19     
- Misses          9       11       +2     
- Partials        0        2       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/helpers/search-user.helper.ts (1)

8-17: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix getUserRealName return type to include null.

The function returns null on multiple paths (Line 10, Line 15) but is typed as string. This can break callers relying on the declared type.
Use : string | null (or return empty string consistently).

As per coding guidelines, “Use specific types and avoid any in TypeScript.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/helpers/search-user.helper.ts` around lines 8 - 17, Update the return
type of getUserRealName to reflect that it can return null by changing its
signature to return string | null (or alternately make all early returns return
'' consistently), and ensure callers handle the nullable result; locate the
function getUserRealName in src/helpers/search-user.helper.ts and adjust its
signature and any dependent usages to accommodate the new nullable type.
🧹 Nitpick comments (1)
src/helpers/global.helper.ts (1)

48-61: ⚡ Quick win

URL-parsing logic is duplicated from parseIdFromUrl.

The URL branch in extractId (the reverse-loop + lang-prefix fallback) is identical to the body of parseIdFromUrl. A future fix to one won't automatically apply to the other. The URL branch can delegate directly:

♻️ Proposed simplification
-  // If it's a URL
-  if (str.includes('/')) {
-    const parts = str.split('/');
-    for (let i = parts.length - 1; i >= 0; i--) {
-      const p = parts[i];
-      if (/^\d+-/.test(p)) {
-        return +p.split('-')[0] || null;
-      }
-    }
-    // Fallback logic for URL
-    const hasLangPrefix = LANG_PREFIX_REGEX.test(parts[1]);
-    const idSlug = parts[hasLangPrefix ? 3 : 2];
-    const id = idSlug?.split('-')[0];
-    return +id || null;
-  }
+  // If it's a URL
+  if (str.includes('/')) {
+    return parseIdFromUrl(str);
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/helpers/global.helper.ts` around lines 48 - 61, The URL-parsing branch in
extractId duplicates the logic from parseIdFromUrl; replace the entire if
(str.includes('/')) { ... } block in extractId with a direct delegation to
parseIdFromUrl(str) (preserving the same return semantics, i.e., return the
numeric id or null). Use the existing parseIdFromUrl function and keep
references like LANG_PREFIX_REGEX untouched there, so extractId simply calls
parseIdFromUrl(str) and returns its result.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/services/user-ratings.service.ts`:
- Line 25: The line using a silent fallback ("const id = extractId(user) ??
user;") should be replaced with an explicit guard like in creator.service.ts and
movie.service.ts: call extractId(user), if it returns null/undefined throw an
Error (or return a handled error) rather than using the raw input; update the
code path in user-ratings.service.ts to ensure the function that currently
declares const id uses this guard so downstream logic always receives a
validated id (reference the extractId helper and the const id binding to locate
the code to change).

In `@src/services/user-reviews.service.ts`:
- Line 27: The current assignment const id = extractId(user) ?? user silently
uses the raw input when extractId(user) returns null; change this to throw a
descriptive error instead so invalid inputs fail fast (matching
creator.service.ts and movie.service.ts). Specifically, in
user-reviews.service.ts where id is computed (the code using extractId and the
id variable), check the result of extractId(user) and if it is null/undefined
throw a new Error with a clear message (e.g., "Invalid user id: <user>") rather
than falling back to the raw user string; ensure the thrown message mirrors the
style used in creator.service.ts/movie.service.ts for consistency.

In `@src/vars.ts`:
- Around line 39-44: The ternary checks in the URL builders treat 0 as falsy and
drop the page query; update the conditional in both userReviewsUrl and the
companion user ratings function (the lines building `${userUrl(user,
options)}/hodnoceni/${page ? '?page=' + page : ''}` and `${userUrl(user,
options)}/recenze/${page ? '?page=' + page : ''}`) to explicitly test for
presence (e.g., use `page !== undefined` or `page != null`) so page=0 is
included in the generated query string.

In `@test-dx-replace.ts`:
- Line 1: Delete the accidental scratch file test-dx-replace.ts entirely (it
only contains an invalid import of extractId) and remove any references to it in
the commit/exports; do not try to fix the import or enable
allowImportingTsExtensions—the project uses .js ESM imports, so the correct
action is to remove this debugging artifact and ensure no other modules import
test-dx-replace.ts or reference its import of extractId.

---

Outside diff comments:
In `@src/helpers/search-user.helper.ts`:
- Around line 8-17: Update the return type of getUserRealName to reflect that it
can return null by changing its signature to return string | null (or
alternately make all early returns return '' consistently), and ensure callers
handle the nullable result; locate the function getUserRealName in
src/helpers/search-user.helper.ts and adjust its signature and any dependent
usages to accommodate the new nullable type.

---

Nitpick comments:
In `@src/helpers/global.helper.ts`:
- Around line 48-61: The URL-parsing branch in extractId duplicates the logic
from parseIdFromUrl; replace the entire if (str.includes('/')) { ... } block in
extractId with a direct delegation to parseIdFromUrl(str) (preserving the same
return semantics, i.e., return the numeric id or null). Use the existing
parseIdFromUrl function and keep references like LANG_PREFIX_REGEX untouched
there, so extractId simply calls parseIdFromUrl(str) and returns its result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e2353b54-a6e4-48f8-928d-5ad2abaec3b2

📥 Commits

Reviewing files that changed from the base of the PR and between a1c5c20 and c21de1b.

📒 Files selected for processing (16)
  • src/bin/export-reviews.ts
  • src/bin/lookup-movie.ts
  • src/bin/search.ts
  • src/bin/utils.ts
  • src/dto/options.ts
  • src/helpers/global.helper.ts
  • src/helpers/search-user.helper.ts
  • src/index.ts
  • src/services/creator.service.ts
  • src/services/movie.service.ts
  • src/services/user-ratings.service.ts
  • src/services/user-reviews.service.ts
  • src/types.ts
  • src/vars.ts
  • test-dx-replace.ts
  • tests/helpers.test.ts

config?: CSFDUserRatingConfig,
options?: CSFDOptions
): Promise<CSFDUserRatings[]> {
const id = extractId(user) ?? user;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Same silent-fallback inconsistency as user-reviews.service.ts.

extractId(user) ?? user silently falls back to the raw string on invalid input rather than throwing early. Apply the same guard pattern used in creator.service.ts and movie.service.ts.

🛡️ Proposed fix
-    const id = extractId(user) ?? user;
+    const id = extractId(user);
+    if (!id) {
+      throw new Error('node-csfd-api: user must be a valid number, slug, or URL');
+    }
📝 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 id = extractId(user) ?? user;
const id = extractId(user);
if (!id) {
throw new Error('node-csfd-api: user must be a valid number, slug, or URL');
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/user-ratings.service.ts` at line 25, The line using a silent
fallback ("const id = extractId(user) ?? user;") should be replaced with an
explicit guard like in creator.service.ts and movie.service.ts: call
extractId(user), if it returns null/undefined throw an Error (or return a
handled error) rather than using the raw input; update the code path in
user-ratings.service.ts to ensure the function that currently declares const id
uses this guard so downstream logic always receives a validated id (reference
the extractId helper and the const id binding to locate the code to change).

config?: CSFDUserReviewsConfig,
options?: CSFDOptions
): Promise<CSFDUserReviews[]> {
const id = extractId(user) ?? user;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Silent fallback on invalid input is inconsistent with other services.

extractId(user) ?? user silently proceeds with the raw string when extractId returns null (e.g., for 'blade-runner'), constructing a malformed URL and resulting in a confusing server error instead of an early validation failure. creator.service.ts and movie.service.ts both throw a descriptive error in this case — the same pattern should be applied here.

🛡️ Proposed fix to align with the throwing pattern
-    const id = extractId(user) ?? user;
+    const id = extractId(user);
+    if (!id) {
+      throw new Error('node-csfd-api: user must be a valid number, slug, or URL');
+    }
📝 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 id = extractId(user) ?? user;
const id = extractId(user);
if (!id) {
throw new Error('node-csfd-api: user must be a valid number, slug, or URL');
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/user-reviews.service.ts` at line 27, The current assignment
const id = extractId(user) ?? user silently uses the raw input when
extractId(user) returns null; change this to throw a descriptive error instead
so invalid inputs fail fast (matching creator.service.ts and movie.service.ts).
Specifically, in user-reviews.service.ts where id is computed (the code using
extractId and the id variable), check the result of extractId(user) and if it is
null/undefined throw a new Error with a clear message (e.g., "Invalid user id:
<user>") rather than falling back to the raw user string; ensure the thrown
message mirrors the style used in creator.service.ts/movie.service.ts for
consistency.

Comment thread src/vars.ts
Comment on lines +39 to +44
): string => `${userUrl(user, options)}/hodnoceni/${page ? '?page=' + page : ''}`;
export const userReviewsUrl = (
user: string | number,
page?: number,
options: Options = {}
): string => `${userUrl(user, options)}/recenze/${page ? '?page=' + page : ''}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use explicit page presence checks instead of truthiness.

On Line 39 and Line 44, page ? ... : '' treats 0 as “not provided.” Use page !== undefined (or page != null) to avoid silent query omission.

Proposed fix
 export const userRatingsUrl = (
   user: string | number,
   page?: number,
   options: Options = {}
-): string => `${userUrl(user, options)}/hodnoceni/${page ? '?page=' + page : ''}`;
+): string => `${userUrl(user, options)}/hodnoceni/${page !== undefined ? '?page=' + page : ''}`;
 export const userReviewsUrl = (
   user: string | number,
   page?: number,
   options: Options = {}
-): string => `${userUrl(user, options)}/recenze/${page ? '?page=' + page : ''}`;
+): string => `${userUrl(user, options)}/recenze/${page !== undefined ? '?page=' + page : ''}`;
📝 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
): string => `${userUrl(user, options)}/hodnoceni/${page ? '?page=' + page : ''}`;
export const userReviewsUrl = (
user: string | number,
page?: number,
options: Options = {}
): string => `${userUrl(user, options)}/recenze/${page ? '?page=' + page : ''}`;
): string => `${userUrl(user, options)}/hodnoceni/${page !== undefined ? '?page=' + page : ''}`;
export const userReviewsUrl = (
user: string | number,
page?: number,
options: Options = {}
): string => `${userUrl(user, options)}/recenze/${page !== undefined ? '?page=' + page : ''}`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/vars.ts` around lines 39 - 44, The ternary checks in the URL builders
treat 0 as falsy and drop the page query; update the conditional in both
userReviewsUrl and the companion user ratings function (the lines building
`${userUrl(user, options)}/hodnoceni/${page ? '?page=' + page : ''}` and
`${userUrl(user, options)}/recenze/${page ? '?page=' + page : ''}`) to
explicitly test for presence (e.g., use `page !== undefined` or `page != null`)
so page=0 is included in the generated query string.

Comment thread test-dx-replace.ts
@@ -0,0 +1 @@
import { extractId } from './src/helpers/global.helper.ts'; // Doesn't exist
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove this accidentally committed scratch file.

test-dx-replace.ts is a one-line debugging artifact with a stale // Doesn't exist comment (the export now exists) and no functionality. It should not be shipped as part of this PR. Additionally, importing with a .ts extension requires allowImportingTsExtensions: true in tsconfig and cannot emit compiled output, which deviates from the project's established .js import convention for ESM.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test-dx-replace.ts` at line 1, Delete the accidental scratch file
test-dx-replace.ts entirely (it only contains an invalid import of extractId)
and remove any references to it in the commit/exports; do not try to fix the
import or enable allowImportingTsExtensions—the project uses .js ESM imports, so
the correct action is to remove this debugging artifact and ensure no other
modules import test-dx-replace.ts or reference its import of extractId.

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.

2 participants