Skip to content

Commit 4e5e471

Browse files
committed
Move fetch_options, expand tests, fix inline notes
1 parent cd8c885 commit 4e5e471

File tree

4 files changed

+543
-223
lines changed

4 files changed

+543
-223
lines changed

src/gleam/fetch.gleam

Lines changed: 281 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import gleam/dynamic.{type Dynamic}
2-
import gleam/fetch/fetch_options.{type FetchOptions}
32
import gleam/fetch/form_data.{type FormData}
43
import gleam/http/request.{type Request}
54
import gleam/http/response.{type Response}
@@ -46,6 +45,13 @@ pub fn raw_send(
4645
request: FetchRequest,
4746
) -> Promise(Result(FetchResponse, FetchError))
4847

48+
/// Bridge for Request with FetchOptions between Gleam and JavaScript.
49+
@external(javascript, "../gleam_fetch_ffi.mjs", "raw_send")
50+
fn raw_send_converted_options(
51+
request: FetchRequest,
52+
options: FetchOptionsNative,
53+
) -> Promise(Result(FetchResponse, FetchError))
54+
4955
/// Call directly `fetch` with a `Request` and `FetchOptions`,
5056
/// then convert the result back to Gleam.
5157
/// Let you get back a `FetchResponse` instead of the Gleam
@@ -56,13 +62,24 @@ pub fn raw_send(
5662
/// |> request.set_host("example.com")
5763
/// |> request.set_path("/example")
5864
/// |> fetch.to_fetch_request
59-
/// |> fetch.raw_send_with(fetch_options.new())
65+
/// |> fetch.raw_send_options(fetch_options.new())
6066
/// ```
61-
@external(javascript, "../gleam_fetch_ffi.mjs", "raw_send")
62-
pub fn raw_send_with(
67+
pub fn raw_send_options(
6368
request: FetchRequest,
6469
options: FetchOptions,
65-
) -> Promise(Result(FetchResponse, FetchError))
70+
) -> Promise(Result(FetchResponse, FetchError)) {
71+
raw_send_converted_options(
72+
request,
73+
FetchOptionsNative(
74+
cache: cache_to_string(options.cache),
75+
credentials: credentials_to_string(options.credentials),
76+
keepalive: options.keepalive,
77+
mode: cors_to_string(options.mode),
78+
priority: priority_to_string(options.priority),
79+
redirect: redirect_to_string(options.redirect),
80+
),
81+
)
82+
}
6683

6784
/// Call `fetch` with a Gleam `Request(String)`, and convert the result back
6885
/// to Gleam. Use it to send strings or JSON stringified.
@@ -95,7 +112,7 @@ pub fn send(
95112
/// Use it to send strings or JSON stringified.
96113
///
97114
/// If you're looking for something more low-level, take a look at
98-
/// [`raw_send_with`](#raw_send_with).
115+
/// [`raw_send_options`](#raw_send_options).
99116
///
100117
/// ```gleam
101118
/// let my_data = json.object([#("field", "value")])
@@ -104,15 +121,15 @@ pub fn send(
104121
/// |> request.set_path("/example")
105122
/// |> request.set_body(json.to_string(my_data))
106123
/// |> request.set_header("content-type", "application/json")
107-
/// |> fetch.send_with(fetch_options.new())
124+
/// |> fetch.send_options(fetch_options.new())
108125
/// ```
109-
pub fn send_with(
126+
pub fn send_options(
110127
request: Request(String),
111128
options: FetchOptions,
112129
) -> Promise(Result(Response(FetchBody), FetchError)) {
113130
request
114131
|> to_fetch_request
115-
|> raw_send_with(options)
132+
|> raw_send_options(options)
116133
|> promise.try_await(fn(resp) {
117134
promise.resolve(Ok(from_fetch_response(resp)))
118135
})
@@ -152,7 +169,7 @@ pub fn send_form_data(
152169
/// decoded as-is on servers.
153170
///
154171
/// If you're looking for something more low-level, take a look at
155-
/// [`raw_send_with`](#raw_send_with).
172+
/// [`raw_send_options`](#raw_send_options).
156173
///
157174
/// ```gleam
158175
/// request.new()
@@ -162,15 +179,15 @@ pub fn send_form_data(
162179
/// form_data.new()
163180
/// |> form_data.append("key", "value")
164181
/// })
165-
/// |> fetch.send_form_data_with(fetch_options.new())
182+
/// |> fetch.send_form_data_options(fetch_options.new())
166183
/// ```
167-
pub fn send_form_data_with(
184+
pub fn send_form_data_options(
168185
request: Request(FormData),
169186
options: FetchOptions,
170187
) -> Promise(Result(Response(FetchBody), FetchError)) {
171188
request
172189
|> form_data_to_fetch_request
173-
|> raw_send_with(options)
190+
|> raw_send_options(options)
174191
|> promise.try_await(fn(resp) {
175192
promise.resolve(Ok(from_fetch_response(resp)))
176193
})
@@ -207,23 +224,23 @@ pub fn send_bits(
207224
/// and you probably want a proper content-type added.
208225
///
209226
/// If you're looking for something more low-level, take a look at
210-
/// [`raw_send_with`](#raw_send_with).
227+
/// [`raw_send_options`](#raw_send_options).
211228
///
212229
/// ```gleam
213230
/// request.new()
214231
/// |> request.set_host("example.com")
215232
/// |> request.set_path("/example")
216233
/// |> request.set_body(<<"data">>)
217234
/// |> request.set_header("content-type", "application/octet-stream")
218-
/// |> fetch.send_bits_with(fetch_options.new())
235+
/// |> fetch.send_bits_options(fetch_options.new())
219236
/// ```
220-
pub fn send_bits_with(
237+
pub fn send_bits_options(
221238
request: Request(BitArray),
222239
options: FetchOptions,
223240
) -> Promise(Result(Response(FetchBody), FetchError)) {
224241
request
225242
|> bitarray_request_to_fetch_request
226-
|> raw_send_with(options)
243+
|> raw_send_options(options)
227244
|> promise.try_await(fn(resp) {
228245
promise.resolve(Ok(from_fetch_response(resp)))
229246
})
@@ -363,3 +380,250 @@ pub fn read_text_body(
363380
pub fn read_json_body(
364381
a: Response(FetchBody),
365382
) -> Promise(Result(Response(Dynamic), FetchError))
383+
384+
/// Gleam equivalent of JavaScript
385+
/// [`RequestInit`](https://developer.mozilla.org/docs/Web/API/RequestInit).
386+
///
387+
/// The Node target supports only the `redirect` and `priority` options.
388+
pub opaque type FetchOptions {
389+
Builder(
390+
cache: Cache,
391+
credentials: Credentials,
392+
keepalive: Bool,
393+
mode: Cors,
394+
priority: Priority,
395+
redirect: Redirect,
396+
)
397+
}
398+
399+
// Converted internal FetchOptions to be send via JavaScript.
400+
type FetchOptionsNative {
401+
FetchOptionsNative(
402+
cache: String,
403+
credentials: String,
404+
keepalive: Bool,
405+
mode: String,
406+
priority: String,
407+
redirect: String,
408+
)
409+
}
410+
411+
/// Cache options, for details see
412+
/// [`cache`](https://developer.mozilla.org/docs/Web/API/RequestInit#cache).
413+
///
414+
/// Change how responses are stored and retrieved from cache.
415+
pub type Cache {
416+
/// Default cache behaviour.
417+
///
418+
/// Fresh record will be returned from the cache.
419+
/// If the record in cache is stale and server responds with not
420+
/// changed, then the value from cache is used. Otherwise makes normal
421+
/// request and updates the cache.
422+
Default
423+
/// Response is not fetched from the cache and not stored in the cache.
424+
NoStore
425+
/// Response is not fetched from the cache but gets stored.
426+
Reload
427+
/// If the record in cache is fresh or stale and server responds with not
428+
/// changed, then the value from cache is used. Otherwise makes normal
429+
/// request and updates the cache.
430+
NoCache
431+
/// If record is in cache, it is always used. Otherwise makes normal
432+
/// request.
433+
ForceCache
434+
}
435+
436+
fn cache_to_string(cache: Cache) -> String {
437+
case cache {
438+
Default -> "default"
439+
NoStore -> "no-store"
440+
Reload -> "reload"
441+
NoCache -> "no-cache"
442+
ForceCache -> "force-cache"
443+
}
444+
}
445+
446+
/// Credentials options, for details see
447+
/// [`credentials`](https://developer.mozilla.org/docs/Web/API/RequestInit#credentials).
448+
///
449+
/// Control whether browser sends credentials with the request and whether
450+
/// Set-Cookie response headers are respected.
451+
pub type Credentials {
452+
/// Never send credentials or include credentials in the response.
453+
CredentialsOmit
454+
/// Only send and include credentials for same-origin requests.
455+
CredentialsSameOrigin
456+
/// Always include credentials.
457+
CredentialsInclude
458+
}
459+
460+
fn credentials_to_string(credentials: Credentials) -> String {
461+
case credentials {
462+
CredentialsOmit -> "omit"
463+
CredentialsSameOrigin -> "same-origin"
464+
CredentialsInclude -> "include"
465+
}
466+
}
467+
468+
/// CORS options, for details see
469+
/// [`mode`](https://developer.mozilla.org/docs/Web/API/RequestInit#mode).
470+
///
471+
/// Set cross-origin behaviour of a request.
472+
pub type Cors {
473+
/// Disallows cross-origin requests.
474+
SameOrigin
475+
/// Defaults to
476+
/// [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS)
477+
/// mechanism.
478+
Cors
479+
/// Disables CORS for cross-origin requests.
480+
NoCors
481+
/// Used only by HTML navigation.
482+
Navigate
483+
}
484+
485+
fn cors_to_string(cors: Cors) -> String {
486+
case cors {
487+
SameOrigin -> "same-origin"
488+
Cors -> "cors"
489+
NoCors -> "no-cors"
490+
Navigate -> "navigate"
491+
}
492+
}
493+
494+
/// Priority options, for details see
495+
/// [`priority`](https://developer.mozilla.org/docs/Web/API/RequestInit#priority).
496+
///
497+
/// Increase priority of a request relative to other requests.
498+
pub type Priority {
499+
/// Higher priority.
500+
High
501+
/// Lower priority.
502+
Low
503+
/// No preference of priority.
504+
Auto
505+
}
506+
507+
fn priority_to_string(priority: Priority) -> String {
508+
case priority {
509+
High -> "high"
510+
Low -> "low"
511+
Auto -> "auto"
512+
}
513+
}
514+
515+
/// Redirect options, for details see
516+
/// [`redirect`](https://developer.mozilla.org/docs/Web/API/RequestInit#redirect).
517+
///
518+
/// Change the redirect behaviour of a request.
519+
pub type Redirect {
520+
/// Automatically redirects request.
521+
Follow
522+
/// Errors out on redirect.
523+
Error
524+
/// Expects user to handle redirects manually.
525+
Manual
526+
}
527+
528+
fn redirect_to_string(redirect: Redirect) -> String {
529+
case redirect {
530+
Follow -> "follow"
531+
Error -> "error"
532+
Manual -> "manual"
533+
}
534+
}
535+
536+
/// Creates new `FetchOptions` object with default values.
537+
///
538+
/// Useful if more precise control over fetch is required, such as using
539+
/// signals, cache options and so on.
540+
///
541+
/// ```gleam
542+
/// let options = fetch_options.new()
543+
/// |> fetch_options.cache(fetch_options.NoStore)
544+
/// ```
545+
pub fn fetch_options() -> FetchOptions {
546+
Builder(
547+
cache: Default,
548+
credentials: CredentialsSameOrigin,
549+
keepalive: False,
550+
mode: Cors,
551+
priority: Auto,
552+
redirect: Follow,
553+
)
554+
}
555+
556+
/// Set the
557+
/// [`cache`](https://developer.mozilla.org/docs/Web/API/RequestInit#cache)
558+
/// option of `FetchOptions`.
559+
///
560+
/// ```gleam
561+
/// let options = fetch_options.new()
562+
/// |> fetch_options.cache(fetch_options.NoStore)
563+
/// ```
564+
pub fn cache(fetch_options: FetchOptions, which: Cache) -> FetchOptions {
565+
Builder(..fetch_options, cache: which)
566+
}
567+
568+
/// Set the
569+
/// [`credentials`](https://developer.mozilla.org/docs/Web/API/RequestInit#credentials)
570+
/// option of `FetchOptions`.
571+
///
572+
/// ```gleam
573+
/// let options = fetch_options.new()
574+
/// |> fetch_options.credentials(fetch_options.CredentialsOmit)
575+
/// ```
576+
pub fn credentials(
577+
fetch_options: FetchOptions,
578+
which: Credentials,
579+
) -> FetchOptions {
580+
Builder(..fetch_options, credentials: which)
581+
}
582+
583+
/// Set the
584+
/// [`keepalive`](https://developer.mozilla.org/docs/Web/API/RequestInit#keepalive)
585+
/// option of `FetchOptions`.
586+
///
587+
/// ```gleam
588+
/// let options = fetch_options.new()
589+
/// |> fetch_options.keepalive(True)
590+
/// ```
591+
pub fn keepalive(fetch_options: FetchOptions, keepalive: Bool) -> FetchOptions {
592+
Builder(..fetch_options, keepalive: keepalive)
593+
}
594+
595+
/// Set the
596+
/// [`cors`](https://developer.mozilla.org/docs/Web/API/RequestInit#mode)
597+
/// option of `FetchOptions`.
598+
///
599+
/// ```gleam
600+
/// let options = fetch_options.new()
601+
/// |> fetch_options.cors(fetch_options.SameOrigin)
602+
/// ```
603+
pub fn cors(fetch_options: FetchOptions, which: Cors) -> FetchOptions {
604+
Builder(..fetch_options, mode: which)
605+
}
606+
607+
/// Set the
608+
/// [`priority`](https://developer.mozilla.org/docs/Web/API/RequestInit#priority)
609+
/// option of `FetchOptions`.
610+
///
611+
/// ```gleam
612+
/// let options = fetch_options.new()
613+
/// |> fetch_options.cors(fetch_options.High)
614+
/// ```
615+
pub fn priority(fetch_options: FetchOptions, which: Priority) -> FetchOptions {
616+
Builder(..fetch_options, priority: which)
617+
}
618+
619+
/// Set the
620+
/// [`redirect`](https://developer.mozilla.org/docs/Web/API/RequestInit#redirect)
621+
/// option of `FetchOptions`.
622+
///
623+
/// ```gleam
624+
/// let options = fetch_options.new()
625+
/// |> fetch_options.redirect(fetch_options.Follow)
626+
/// ```
627+
pub fn redirect(fetch_options: FetchOptions, which: Redirect) -> FetchOptions {
628+
Builder(..fetch_options, redirect: which)
629+
}

0 commit comments

Comments
 (0)