Skip to content

Commit 490797c

Browse files
authored
Merge pull request #61 from ChangemakerStudios/feature/Cookies
Add strongly-typed cookie support for Gotenberg API. Addresses issue #59.
2 parents f1a8fc3 + 763ce10 commit 490797c

File tree

10 files changed

+720
-73
lines changed

10 files changed

+720
-73
lines changed

lib/Domain/Builders/Faceted/DocumentBuilder.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@ public sealed class DocumentBuilder
2626

2727
public DocumentBuilder(FullDocument content, Action<bool> setContainsMarkdown)
2828
{
29-
if (content == null) throw new ArgumentNullException(nameof(content));
30-
31-
this._content = content;
29+
this._content = content ?? throw new ArgumentNullException(nameof(content));
3230
this._setContainsMarkdown = setContainsMarkdown;
3331
}
3432

lib/Domain/Builders/Faceted/HtmlConversionBehaviorBuilder.cs

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public sealed class HtmlConversionBehaviorBuilder
2323

2424
internal HtmlConversionBehaviorBuilder(HtmlConversionBehaviors htmlConversionBehaviors)
2525
{
26-
this._htmlConversionBehaviors = htmlConversionBehaviors;
26+
_htmlConversionBehaviors = htmlConversionBehaviors;
2727
}
2828

2929
/// <summary>
@@ -34,7 +34,7 @@ internal HtmlConversionBehaviorBuilder(HtmlConversionBehaviors htmlConversionBeh
3434
/// <remarks>Prefer <see cref="SetBrowserWaitExpression" /> over waitDelay.</remarks>
3535
public HtmlConversionBehaviorBuilder SetBrowserWaitDelay(int seconds)
3636
{
37-
this._htmlConversionBehaviors.WaitDelay = $"{seconds}s";
37+
_htmlConversionBehaviors.WaitDelay = $"{seconds}s";
3838

3939
return this;
4040
}
@@ -49,9 +49,12 @@ public HtmlConversionBehaviorBuilder SetBrowserWaitDelay(int seconds)
4949
/// <exception cref="InvalidOperationException"></exception>
5050
public HtmlConversionBehaviorBuilder SetBrowserWaitExpression(string expression)
5151
{
52-
if (expression.IsNotSet()) throw new InvalidOperationException("expression is not set");
52+
if (expression.IsNotSet())
53+
{
54+
throw new InvalidOperationException("expression is not set");
55+
}
5356

54-
this._htmlConversionBehaviors.WaitForExpression = expression;
57+
_htmlConversionBehaviors.WaitForExpression = expression;
5558

5659
return this;
5760
}
@@ -65,9 +68,12 @@ public HtmlConversionBehaviorBuilder SetBrowserWaitExpression(string expression)
6568
[Obsolete("Deprecated in Gotenberg v8+")]
6669
public HtmlConversionBehaviorBuilder SetUserAgent(string userAgent)
6770
{
68-
if (userAgent.IsNotSet()) throw new InvalidOperationException("headerName is not set");
71+
if (userAgent.IsNotSet())
72+
{
73+
throw new InvalidOperationException("headerName is not set");
74+
}
6975

70-
this._htmlConversionBehaviors.UserAgent = userAgent;
76+
_htmlConversionBehaviors.UserAgent = userAgent;
7177

7278
return this;
7379
}
@@ -84,7 +90,7 @@ public HtmlConversionBehaviorBuilder AddAdditionalHeaders(string headerName, str
8490
{
8591
var header = string.Format("{0}{2}{1}", "{", "}", $"{'"'}{headerName}{'"'} : {'"'}{headerValue}{'"'}");
8692

87-
return this.AddAdditionalHeaders(JObject.Parse(header));
93+
return AddAdditionalHeaders(JObject.Parse(header));
8894
}
8995

9096
/// <summary>
@@ -95,16 +101,42 @@ public HtmlConversionBehaviorBuilder AddAdditionalHeaders(string headerName, str
95101
/// <exception cref="InvalidOperationException"></exception>
96102
public HtmlConversionBehaviorBuilder AddAdditionalHeaders(JObject extraHeaders)
97103
{
98-
if (extraHeaders == null) throw new InvalidOperationException("headerValue is null");
104+
if (extraHeaders == null)
105+
{
106+
throw new InvalidOperationException("extraHeaders is null");
107+
}
99108

100-
this._htmlConversionBehaviors.ExtraHeaders = extraHeaders;
109+
_htmlConversionBehaviors.ExtraHeaders = extraHeaders;
101110

102111
return this;
103112
}
104113

105114
/// <summary>
106-
/// Sets the document metadata.
107-
/// Not all metadata are writable. Consider taking a look at https://exiftool.org/TagNames/XMP.html#pdf for an (exhaustive?) list of available metadata.
115+
/// Adds a cookie to store in the Chromium cookie jar.
116+
/// </summary>
117+
/// <param name="cookie">The cookie to add</param>
118+
/// <returns></returns>
119+
/// <exception cref="ArgumentNullException"></exception>
120+
public HtmlConversionBehaviorBuilder AddCookie(Cookie cookie)
121+
{
122+
if (cookie == null)
123+
{
124+
throw new ArgumentNullException(nameof(cookie));
125+
}
126+
127+
_htmlConversionBehaviors.Cookies ??= new List<Cookie>();
128+
129+
Cookie.Validate(cookie);
130+
131+
_htmlConversionBehaviors.Cookies.Add(cookie);
132+
133+
return this;
134+
}
135+
136+
/// <summary>
137+
/// Sets the document metadata.
138+
/// Not all metadata are writable. Consider taking a look at https://exiftool.org/TagNames/XMP.html#pdf for an
139+
/// (exhaustive?) list of available metadata.
108140
/// </summary>
109141
/// <param name="dictionary"></param>
110142
/// <returns></returns>
@@ -116,16 +148,20 @@ public HtmlConversionBehaviorBuilder SetMetadata(IDictionary<string, object> dic
116148
}
117149

118150
/// <summary>
119-
/// Sets the document metadata.
120-
/// Not all metadata are writable. Consider taking a look at https://exiftool.org/TagNames/XMP.html#pdf for an (exhaustive?) list of available metadata.
151+
/// Sets the document metadata.
152+
/// Not all metadata are writable. Consider taking a look at https://exiftool.org/TagNames/XMP.html#pdf for an
153+
/// (exhaustive?) list of available metadata.
121154
/// </summary>
122155
/// <param name="metadata"></param>
123156
/// <returns></returns>
124157
public HtmlConversionBehaviorBuilder SetMetadata(JObject metadata)
125158
{
126-
if (metadata == null) throw new InvalidOperationException("metadata is null");
159+
if (metadata == null)
160+
{
161+
throw new InvalidOperationException("metadata is null");
162+
}
127163

128-
this._htmlConversionBehaviors.MetaData = metadata;
164+
_htmlConversionBehaviors.MetaData = metadata;
129165

130166
return this;
131167
}
@@ -136,7 +172,7 @@ public HtmlConversionBehaviorBuilder SetMetadata(JObject metadata)
136172
/// <returns></returns>
137173
public HtmlConversionBehaviorBuilder FailOnConsoleExceptions()
138174
{
139-
this._htmlConversionBehaviors.FailOnConsoleExceptions = true;
175+
_htmlConversionBehaviors.FailOnConsoleExceptions = true;
140176

141177
return this;
142178
}
@@ -147,18 +183,18 @@ public HtmlConversionBehaviorBuilder FailOnConsoleExceptions()
147183
/// <returns></returns>
148184
public HtmlConversionBehaviorBuilder EmulateAsScreen()
149185
{
150-
this._htmlConversionBehaviors.EmulatedMediaType = "screen";
186+
_htmlConversionBehaviors.EmulatedMediaType = "screen";
151187

152188
return this;
153189
}
154190

155191
/// <summary>
156-
/// Gotenberg 8+ ONLY: Configures gotenberg to not wait for Chromium network to be idle.
192+
/// Gotenberg 8+ ONLY: Configures gotenberg to not wait for Chromium network to be idle.
157193
/// </summary>
158194
/// <returns></returns>
159195
public HtmlConversionBehaviorBuilder SkipNetworkIdleEvent()
160196
{
161-
this._htmlConversionBehaviors.SkipNetworkIdleEvent = true;
197+
_htmlConversionBehaviors.SkipNetworkIdleEvent = true;
162198

163199
return this;
164200
}
@@ -171,9 +207,12 @@ public HtmlConversionBehaviorBuilder SkipNetworkIdleEvent()
171207
/// <exception cref="InvalidOperationException"></exception>
172208
public HtmlConversionBehaviorBuilder SetPdfFormat(ConversionPdfFormats format)
173209
{
174-
if (format == default) throw new InvalidOperationException("Invalid PDF format specified");
210+
if (format == default)
211+
{
212+
throw new InvalidOperationException("Invalid PDF format specified");
213+
}
175214

176-
this._htmlConversionBehaviors.PdfFormat = format;
215+
_htmlConversionBehaviors.PdfFormat = format;
177216

178217
return this;
179218
}
@@ -183,7 +222,7 @@ public HtmlConversionBehaviorBuilder SetPdfFormat(ConversionPdfFormats format)
183222
/// </summary>
184223
public HtmlConversionBehaviorBuilder SetPdfUa(bool enablePdfUa = true)
185224
{
186-
this._htmlConversionBehaviors.EnablePdfUa = enablePdfUa;
225+
_htmlConversionBehaviors.EnablePdfUa = enablePdfUa;
187226

188227
return this;
189228
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2019-2025 Chris Mohan, Jaben Cargman
2+
// and GotenbergSharpApiClient Contributors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
namespace Gotenberg.Sharp.API.Client.Domain.Builders.Faceted;
17+
18+
public static class HtmlConversionBehaviorBuilderExtensions
19+
{
20+
/// <summary>
21+
/// Adds a cookie to store in the Chromium cookie jar.
22+
/// </summary>
23+
/// <param name="builder">HtmlConversionBehaviorBuilder</param>
24+
/// <param name="name">Cookie name</param>
25+
/// <param name="value">Cookie value</param>
26+
/// <param name="domain">Cookie domain</param>
27+
/// <param name="path">Optional cookie path</param>
28+
/// <param name="secure">Optional secure flag</param>
29+
/// <param name="httpOnly">Optional HTTP-only flag</param>
30+
/// <param name="sameSite">Optional SameSite attribute ("Strict", "Lax", or "None")</param>
31+
/// <returns></returns>
32+
public static HtmlConversionBehaviorBuilder AddCookie(this HtmlConversionBehaviorBuilder builder,
33+
string name,
34+
string value,
35+
string domain,
36+
string? path = null,
37+
bool? secure = null,
38+
bool? httpOnly = null,
39+
string? sameSite = null)
40+
{
41+
if (builder == null)
42+
{
43+
throw new ArgumentNullException(nameof(builder));
44+
}
45+
46+
var cookie = new Cookie
47+
{
48+
Name = name,
49+
Value = value,
50+
Domain = domain,
51+
Path = path,
52+
Secure = secure,
53+
HttpOnly = httpOnly,
54+
SameSite = sameSite
55+
};
56+
57+
return builder.AddCookie(cookie);
58+
}
59+
60+
/// <summary>
61+
/// Adds multiple cookies to store in the Chromium cookie jar.
62+
/// </summary>
63+
/// <param name="builder">HtmlConversionBehaviorBuilder</param>
64+
/// <param name="cookies">The cookies to add</param>
65+
/// <returns></returns>
66+
/// <exception cref="ArgumentNullException"></exception>
67+
public static HtmlConversionBehaviorBuilder AddCookies(this HtmlConversionBehaviorBuilder builder, IEnumerable<Cookie> cookies)
68+
{
69+
if (builder == null)
70+
{
71+
throw new ArgumentNullException(nameof(builder));
72+
}
73+
74+
if (cookies == null)
75+
{
76+
throw new ArgumentNullException(nameof(cookies));
77+
}
78+
79+
foreach (var c in cookies)
80+
{
81+
builder.AddCookie(c);
82+
}
83+
84+
return builder;
85+
}
86+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2019-2025 Chris Mohan, Jaben Cargman
2+
// and GotenbergSharpApiClient Contributors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
using Newtonsoft.Json;
17+
18+
namespace Gotenberg.Sharp.API.Client.Domain.Requests.Facets;
19+
20+
/// <summary>
21+
/// Represents a cookie to store in the Chromium cookie jar.
22+
/// See https://gotenberg.dev/docs/routes#cookies-chromium for details.
23+
/// </summary>
24+
public sealed record Cookie
25+
{
26+
/// <summary>
27+
/// Cookie name (required)
28+
/// </summary>
29+
[JsonProperty("name")]
30+
public required string Name { get; init; }
31+
32+
/// <summary>
33+
/// Cookie value (required)
34+
/// </summary>
35+
[JsonProperty("value")]
36+
public required string Value { get; init; }
37+
38+
/// <summary>
39+
/// Cookie domain (required)
40+
/// </summary>
41+
[JsonProperty("domain")]
42+
public required string Domain { get; init; }
43+
44+
/// <summary>
45+
/// Cookie path (optional)
46+
/// </summary>
47+
[JsonProperty("path", NullValueHandling = NullValueHandling.Ignore)]
48+
public string? Path { get; init; }
49+
50+
/// <summary>
51+
/// Set cookie as secure (optional)
52+
/// </summary>
53+
[JsonProperty("secure", NullValueHandling = NullValueHandling.Ignore)]
54+
public bool? Secure { get; init; }
55+
56+
/// <summary>
57+
/// Set cookie as HTTP-only (optional)
58+
/// </summary>
59+
[JsonProperty("httpOnly", NullValueHandling = NullValueHandling.Ignore)]
60+
public bool? HttpOnly { get; init; }
61+
62+
/// <summary>
63+
/// SameSite attribute. Accepted values: "Strict", "Lax", or "None" (optional)
64+
/// </summary>
65+
[JsonProperty("sameSite", NullValueHandling = NullValueHandling.Ignore)]
66+
public string? SameSite { get; init; }
67+
68+
public static void Validate(Cookie cookie)
69+
{
70+
if (cookie.Name.IsNotSet()) throw new ArgumentException("Cookie.Name must not be null or empty", nameof(cookie.Name));
71+
if (cookie.Value.IsNotSet()) throw new ArgumentException("Cookie.Value must not be null or empty", nameof(cookie.Value));
72+
if (cookie.Domain.IsNotSet()) throw new ArgumentException("Cookie.Domain must not be null or empty", nameof(cookie.Domain));
73+
}
74+
}

0 commit comments

Comments
 (0)