Add Chromium screenshot routes (HTML and URL)#74
Conversation
Introduce screenshot support for HTML and URL content with new request hierarchy: ScreenshotRequest (base) -> ScreenshotHtmlRequest, ScreenshotUrlRequest. Parallel builder hierarchy with BaseScreenshotBuilder, ScreenshotHtmlRequestBuilder, ScreenshotUrlRequestBuilder. Add DDD value objects: ScreenshotFormat (png/jpeg/webp), CompressionQuality (0-100), ScreenDimension (positive pixels). Add ScreenshotProperties facet and ScreenshotPropertyBuilder. Add ScreenshotHtmlAsync and ScreenshotUrlAsync methods to GotenbergSharpClient. Include Screenshot example project and README sections.
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 16 minutes and 20 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughAdded screenshot functionality to the Gotenberg Sharp API Client with new value objects for validated dimensions and quality settings, request types for HTML and URL screenshots, fluent builders for configuration, and client methods to execute screenshot operations. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Builder as ScreenshotHtmlRequestBuilder
participant Request as ScreenshotHtmlRequest
participant GotenbergClient as GotenbergSharpClient
participant API as Gotenberg API
Client->>Builder: new ScreenshotHtmlRequestBuilder()
Client->>Builder: AddDocument(action)
Builder->>Request: Configure Content (FullDocument)
Client->>Builder: WithScreenshotProperties(action)
Builder->>Request: Set screenshot properties
Client->>Builder: BuildAsync()
Builder->>Request: Validate & serialize
Builder-->>Client: return Request
Client->>GotenbergClient: ScreenshotHtmlAsync(builder)
GotenbergClient->>Request: CreateApiRequest()
GotenbergClient->>API: POST /forms/chromium/screenshot/html
API-->>GotenbergClient: Stream (PNG bytes)
GotenbergClient-->>Client: return Stream
sequenceDiagram
participant Client
participant Builder as ScreenshotUrlRequestBuilder
participant Request as ScreenshotUrlRequest
participant GotenbergClient as GotenbergSharpClient
participant API as Gotenberg API
Client->>Builder: new ScreenshotUrlRequestBuilder()
Client->>Builder: SetUrl(url)
Builder->>Request: Validate & assign Uri
Client->>Builder: WithScreenshotProperties(action)
Builder->>Request: Set screenshot properties
Client->>Builder: BuildAsync()
Builder->>Request: Validate & serialize
Builder-->>Client: return Request
Client->>GotenbergClient: ScreenshotUrlAsync(builder)
GotenbergClient->>Request: CreateApiRequest()
GotenbergClient->>API: POST /forms/chromium/screenshot/url
API-->>GotenbergClient: Stream (JPEG bytes)
GotenbergClient-->>Client: return Stream
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~28 minutes Possibly Related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
examples/Screenshot/Program.cs (3)
64-69: Response stream should be disposed after use.Same issue as the HTML screenshot—the response stream from
ScreenshotUrlAsyncshould be disposed.Proposed fix
- var response = await sharpClient.ScreenshotUrlAsync(builder); + await using var response = await sharpClient.ScreenshotUrlAsync(builder);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/Screenshot/Program.cs` around lines 64 - 69, The response stream returned by ScreenshotUrlAsync is not disposed; change the code to ensure the response stream is disposed by using an async using for the response (e.g., await using var response = await sharpClient.ScreenshotUrlAsync(builder)) before creating/writing the file, so the response stream is closed after CopyToAsync completes; update the block around ScreenshotUrlAsync, the response variable, and the file write to use async disposal.
44-49: Response stream should be disposed after use.The
Streamreturned byScreenshotHtmlAsyncis not disposed after being copied to the file. While the file stream is properly disposed viaawait using, the response stream will remain open.Proposed fix
- var response = await sharpClient.ScreenshotHtmlAsync(builder); + await using var response = await sharpClient.ScreenshotHtmlAsync(builder);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/Screenshot/Program.cs` around lines 44 - 49, The response Stream returned from sharpClient.ScreenshotHtmlAsync(builder) is not disposed; wrap that stream in a using/await using (e.g., await using var response = await sharpClient.ScreenshotHtmlAsync(builder)) before calling response.CopyToAsync(file) so the response stream is disposed after copy, keeping the existing file creation/await using for the file intact and returning resultPath as before.
28-70: Consider reusing a singleGotenbergSharpClientinstance.Both
ScreenshotFromHtmlandScreenshotFromUrlcreate their own client viaCreateClient(options), which instantiates a newHttpClienteach time. For a console app example, this works, but it's more idiomatic to reuse a singleHttpClient/client instance.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/Screenshot/Program.cs` around lines 28 - 70, Both ScreenshotFromHtml and ScreenshotFromUrl call CreateClient(options) creating a new GotenbergSharpClient/HttpClient each time; change the code to create a single shared GotenbergSharpClient and reuse it instead of instantiating per method — either instantiate one client once (e.g., a static/shared field) and reference it inside ScreenshotFromHtml and ScreenshotFromUrl, or refactor both methods to accept a GotenbergSharpClient parameter; ensure CreateClient(options) is only called once and the same client instance is used for both ScreenshotHtmlAsync and ScreenshotUrlAsync.test/GotenbergSharpClient.Tests/ScreenshotTests.cs (2)
119-133: Consider using async assertions to avoid blocking.Resultcalls.The synchronous
.Resultcalls onReadAsStringAsync()work but can mask exceptions. FluentAssertions supports async operations.Example using async pattern
- httpContents.FirstOrDefault(c => - c.Headers.ContentDisposition?.Name == "width")! - .ReadAsStringAsync().Result.Should().Be("1920"); + var widthContent = httpContents.FirstOrDefault(c => + c.Headers.ContentDisposition?.Name == "width")!; + (await widthContent.ReadAsStringAsync()).Should().Be("1920");This would require making the test method
async Task.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/GotenbergSharpClient.Tests/ScreenshotTests.cs` around lines 119 - 133, Convert the test to use async/await: change the test method signature to async Task and replace blocking .Result calls on ReadAsStringAsync() with await expressions (call ReadAsStringAsync() with await) when asserting values from ToHttpContent() results; for each check that currently uses httpContents.FirstOrDefault(...).ReadAsStringAsync().Result, await ReadAsStringAsync() into a variable and assert with FluentAssertions (e.g., content.Should().Be("...")), keeping references to ToHttpContent() and ReadAsStringAsync().
184-197: Integration tests require running Gotenberg service.The
CreateAuthenticatedClientmethod connects tolocalhost:3000. Consider adding a category or attribute to skip these tests when the service isn't available, or document the CI setup requirements.#!/bin/bash # Check if there are test categories or setup documentation for integration tests rg -n "Category|Explicit|Ignore|Skip" --type=cs test/ fd -i "readme" test/🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/GotenbergSharpClient.Tests/ScreenshotTests.cs` around lines 184 - 197, Tests that call CreateAuthenticatedClient rely on a running Gotenberg service at localhost:3000; mark those tests as integration-only so CI/locals without the service can skip them. Add a test attribute/category (e.g., [Trait("Category","Integration")] for xUnit, [Category("Integration")] for NUnit, or [TestCategory("Integration")] for MSTest) to the test class/methods in ScreenshotTests that use CreateAuthenticatedClient, or add an [Explicit]/[Skip] attribute if your framework supports it; alternatively update project test docs to state that Gotenberg must be running for integration tests (reference symbols: CreateAuthenticatedClient, GotenbergSharpClientOptions, AddGotenbergSharpClient).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@README.md`:
- Around line 495-529: Add the missing using for the ScreenshotFormat enum so
the README examples are copy/paste-ready: update the "Required Using Statements"
section to include the namespace that exposes ScreenshotFormat (used by
ScreenshotHtmlRequestBuilder and ScreenshotUrlRequestBuilder), e.g. add the
appropriate using/import line for the value-object namespace that defines
ScreenshotFormat so the snippets compile without adding extra qualifiers.
In `@src/Gotenberg.Sharp.Api.Client/Domain/Requests/ScreenshotRequest.cs`:
- Around line 26-33: ScreenshotRequest.ToHttpContent must not include
base.ToHttpContent because that adds PdfOutputOptions (PdfFormat, EnablePdfUa,
Flatten, GenerateTaggedPdf, Metadata) which are rejected by Gotenberg v8
screenshot endpoints; remove the call to base.ToHttpContent and instead return
only the allowed parts (e.g., this.ScreenshotProperties.ToHttpContent(),
this.ConversionBehaviors.ToHttpContent(), this.Config.IfNullEmptyContent()).
Also ensure HtmlConversionBehaviors.UserAgent is excluded for screenshot
requests — either omit it when building ConversionBehaviors content or
filter/clear UserAgent before calling ConversionBehaviors.ToHttpContent() so the
deprecated field is never sent.
---
Nitpick comments:
In `@examples/Screenshot/Program.cs`:
- Around line 64-69: The response stream returned by ScreenshotUrlAsync is not
disposed; change the code to ensure the response stream is disposed by using an
async using for the response (e.g., await using var response = await
sharpClient.ScreenshotUrlAsync(builder)) before creating/writing the file, so
the response stream is closed after CopyToAsync completes; update the block
around ScreenshotUrlAsync, the response variable, and the file write to use
async disposal.
- Around line 44-49: The response Stream returned from
sharpClient.ScreenshotHtmlAsync(builder) is not disposed; wrap that stream in a
using/await using (e.g., await using var response = await
sharpClient.ScreenshotHtmlAsync(builder)) before calling
response.CopyToAsync(file) so the response stream is disposed after copy,
keeping the existing file creation/await using for the file intact and returning
resultPath as before.
- Around line 28-70: Both ScreenshotFromHtml and ScreenshotFromUrl call
CreateClient(options) creating a new GotenbergSharpClient/HttpClient each time;
change the code to create a single shared GotenbergSharpClient and reuse it
instead of instantiating per method — either instantiate one client once (e.g.,
a static/shared field) and reference it inside ScreenshotFromHtml and
ScreenshotFromUrl, or refactor both methods to accept a GotenbergSharpClient
parameter; ensure CreateClient(options) is only called once and the same client
instance is used for both ScreenshotHtmlAsync and ScreenshotUrlAsync.
In `@test/GotenbergSharpClient.Tests/ScreenshotTests.cs`:
- Around line 119-133: Convert the test to use async/await: change the test
method signature to async Task and replace blocking .Result calls on
ReadAsStringAsync() with await expressions (call ReadAsStringAsync() with await)
when asserting values from ToHttpContent() results; for each check that
currently uses httpContents.FirstOrDefault(...).ReadAsStringAsync().Result,
await ReadAsStringAsync() into a variable and assert with FluentAssertions
(e.g., content.Should().Be("...")), keeping references to ToHttpContent() and
ReadAsStringAsync().
- Around line 184-197: Tests that call CreateAuthenticatedClient rely on a
running Gotenberg service at localhost:3000; mark those tests as
integration-only so CI/locals without the service can skip them. Add a test
attribute/category (e.g., [Trait("Category","Integration")] for xUnit,
[Category("Integration")] for NUnit, or [TestCategory("Integration")] for
MSTest) to the test class/methods in ScreenshotTests that use
CreateAuthenticatedClient, or add an [Explicit]/[Skip] attribute if your
framework supports it; alternatively update project test docs to state that
Gotenberg must be running for integration tests (reference symbols:
CreateAuthenticatedClient, GotenbergSharpClientOptions,
AddGotenbergSharpClient).
🪄 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: 5bfebafc-be3d-4ce2-9544-0bc006a4ed5f
📒 Files selected for processing (18)
README.mdexamples/Screenshot/Program.csexamples/Screenshot/Screenshot.csprojsrc/Gotenberg.Sharp.Api.Client/Domain/Builders/BaseScreenshotBuilder.cssrc/Gotenberg.Sharp.Api.Client/Domain/Builders/Faceted/ScreenshotPropertyBuilder.cssrc/Gotenberg.Sharp.Api.Client/Domain/Builders/ScreenshotHtmlRequestBuilder.cssrc/Gotenberg.Sharp.Api.Client/Domain/Builders/ScreenshotUrlRequestBuilder.cssrc/Gotenberg.Sharp.Api.Client/Domain/Requests/Facets/FacetBase.cssrc/Gotenberg.Sharp.Api.Client/Domain/Requests/Facets/ScreenshotProperties.cssrc/Gotenberg.Sharp.Api.Client/Domain/Requests/ScreenshotHtmlRequest.cssrc/Gotenberg.Sharp.Api.Client/Domain/Requests/ScreenshotRequest.cssrc/Gotenberg.Sharp.Api.Client/Domain/Requests/ScreenshotUrlRequest.cssrc/Gotenberg.Sharp.Api.Client/Domain/ValueObjects/CompressionQuality.cssrc/Gotenberg.Sharp.Api.Client/Domain/ValueObjects/ScreenDimension.cssrc/Gotenberg.Sharp.Api.Client/Domain/ValueObjects/ScreenshotFormat.cssrc/Gotenberg.Sharp.Api.Client/GotenbergSharpClient.cssrc/Gotenberg.Sharp.Api.Client/Infrastructure/Constants.cstest/GotenbergSharpClient.Tests/ScreenshotTests.cs
| public HtmlConversionBehaviors ConversionBehaviors { get; set; } = new(); | ||
|
|
||
| protected override IEnumerable<HttpContent> ToHttpContent() | ||
| { | ||
| return this.ScreenshotProperties.ToHttpContent() | ||
| .Concat(this.ConversionBehaviors.ToHttpContent()) | ||
| .Concat(this.Config.IfNullEmptyContent()) | ||
| .Concat(base.ToHttpContent()); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether screenshot builders/requests can set or emit PDF/output-specific options.
fd -i 'BaseScreenshotBuilder.cs|BuildRequestBase.cs|ScreenshotRequest.cs|*Screenshot*Builder*.cs|*Screenshot*Request*.cs'
rg -n --iglob '*Screenshot*Builder*.cs' --iglob '*Screenshot*Request*.cs' --iglob '*BuildRequestBase.cs' 'PdfOutput|SetPdfOutputOptions|HtmlConversionBehaviors|ConversionBehaviors'Repository: ChangemakerStudios/GotenbergSharpApiClient
Length of output: 1913
🌐 Web query:
For Gotenberg v8 Chromium screenshot endpoints (/forms/chromium/screenshot/url and /forms/chromium/screenshot/html), which multipart form fields are officially supported? Are waitDelay, waitForExpression, userAgent, extraHttpHeaders, cookies, failOnConsoleExceptions, emulatedMediaType, skipNetworkIdleEvent, pdfa, pdfua, flatten, generateTaggedPdf, and metadata accepted?
💡 Result:
For Gotenberg v8 Chromium screenshot endpoints (/forms/chromium/screenshot/url and /forms/chromium/screenshot/html), the officially supported multipart form fields are those shared across Chromium routes, particularly the rendering and HTTP/networking options documented for screenshot-html and markdown, and referenced as available for screenshot/url. Core viewport fields (explicitly listed): - width (default 800) - height (default 600) - clip (default false) Image format fields: - format (png, jpeg, webp; default png) - quality (0-100 for jpeg; default 100) - omitBackground (default false) - optimizeForSpeed (default false) From navigation/rendering sections (explicitly mentioned or listed in docs for screenshots): - waitDelay - waitForExpression - emulatedMediaType (likely as emulatedMediaFeatures json) - extraHttpHeaders (as HTTP Headers) - cookies (json array) - failOnConsoleExceptions (default false) - skipNetworkIdleEvent PDF-specific fields like pdfa, pdfua, flatten, generateTaggedPdf are NOT supported on screenshot endpoints (they produce images, not PDFs). userAgent is deprecated in v8 and not supported. metadata is supported for image XMP metadata via PDF Engines module. For /screenshot/url, requires 'url' form field (string, required). For /screenshot/html, requires 'files' with index.html (and optional assets). These endpoints function similarly to their PDF counterparts but output images. Evidence from official docs confirms these fields under "Rendering Behavior" (Viewport, Image Format, JavaScript & Dynamic Content with wait options, Emulated Media Features, Wait Delay/Expression/Selector) and "HTTP & Networking" (Cookies, HTTP Headers, etc.). v8 release notes state screenshot routes have "wait before rendering, emulated media type, custom HTTP headers, invalid HTTP status codes, console exceptions". Troubleshooting mentions skipNetworkIdleEvent for screenshots. Deprecated userAgent not available.
Citations:
- 1: https://gotenberg.dev/docs/convert-with-chromium/screenshot-html
- 2: https://newreleases.io/project/github/gotenberg/gotenberg/release/v8.0.0
- 3: https://gotenberg.dev/docs/routes
- 4: https://gotenberg.dev/docs/convert-with-chromium/screenshot-markdown
- 5: https://gotenberg.dev/docs/convert-with-chromium/convert-url-to-pdf
- 6: Feature: Ability to specify emulated device metrics and clipping screenshot in chromium image screenshot route gotenberg/gotenberg#816
- 7: https://gotenberg.dev/docs/troubleshooting
🏁 Script executed:
fd -type f -name "PdfOutputOptions.cs" -o -name "HtmlConversionBehaviors.cs"Repository: ChangemakerStudios/GotenbergSharpApiClient
Length of output: 256
🏁 Script executed:
rg -A 30 "class PdfOutputOptions" --type csRepository: ChangemakerStudios/GotenbergSharpApiClient
Length of output: 7096
🏁 Script executed:
rg -A 30 "class HtmlConversionBehaviors" --type csRepository: ChangemakerStudios/GotenbergSharpApiClient
Length of output: 3977
🏁 Script executed:
cat src/Gotenberg.Sharp.Api.Client/Domain/Requests/Facets/HtmlConversionBehaviors.csRepository: ChangemakerStudios/GotenbergSharpApiClient
Length of output: 3919
🏁 Script executed:
cat src/Gotenberg.Sharp.Api.Client/Domain/Requests/ScreenshotRequest.csRepository: ChangemakerStudios/GotenbergSharpApiClient
Length of output: 1500
Base class's PdfOutputOptions fields leak into screenshot requests, causing 4xx errors.
Line 33 calls base.ToHttpContent(), which includes PdfOutputOptions fields (PdfFormat, EnablePdfUa, Flatten, GenerateTaggedPdf, Metadata). Gotenberg v8 screenshot endpoints reject these PDF-only options. If a user configures any PdfOutputOptions field via the builder, the screenshot request will fail with 4xx.
Additionally, HtmlConversionBehaviors.UserAgent is marked [Obsolete("Deprecated in Gotenberg v8+")] and is not supported by screenshot endpoints.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/Gotenberg.Sharp.Api.Client/Domain/Requests/ScreenshotRequest.cs` around
lines 26 - 33, ScreenshotRequest.ToHttpContent must not include
base.ToHttpContent because that adds PdfOutputOptions (PdfFormat, EnablePdfUa,
Flatten, GenerateTaggedPdf, Metadata) which are rejected by Gotenberg v8
screenshot endpoints; remove the call to base.ToHttpContent and instead return
only the allowed parts (e.g., this.ScreenshotProperties.ToHttpContent(),
this.ConversionBehaviors.ToHttpContent(), this.Config.IfNullEmptyContent()).
Also ensure HtmlConversionBehaviors.UserAgent is excluded for screenshot
requests — either omit it when building ConversionBehaviors content or
filter/clear UserAgent before calling ConversionBehaviors.ToHttpContent() so the
deprecated field is never sent.
Resolve conflicts in FacetBase.cs (keep both screenshot and chromium type serialization cases), GotenbergSharpClient.cs (keep both screenshot and PDF engine methods), and README.md (keep screenshot sections).
Summary
ScreenshotRequest(base) ->ScreenshotHtmlRequest,ScreenshotUrlRequestBaseScreenshotBuilder->ScreenshotHtmlRequestBuilder,ScreenshotUrlRequestBuilderScreenshotPropertiesfacet with device dimensions, format, quality, clip, omitBackground, optimizeForSpeedScreenshotFormat(png/jpeg/webp),CompressionQuality(0-100),ScreenDimension(positive pixels)ScreenshotHtmlAsyncandScreenshotUrlAsyncmethods toGotenbergSharpClient/forms/chromium/screenshot/url,/forms/chromium/screenshot/htmlTest plan
Summary by CodeRabbit
Release Notes
New Features
ScreenshotHtmlAsyncandScreenshotUrlAsyncclient methods with configurable properties (dimensions, format, quality, clipping).Documentation
Examples
Tests