Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fetch support #31

Merged
merged 8 commits into from
Oct 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
docs/
node_modules/
/lib/bs
/lib/js/examples
/lib/js/src
/lib/js/tests/testHelpers.js
npm-debug.log
13 changes: 7 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
### 1.0 in progress

Done:
* Change all `send.pipe` externals to `send`, making them "t-first" (#16)
* Remove deprecated APIs
* `Window.getSelection` now returns an option, to better match the definition
* Add scrollToWithOptions to window
* Convert to rescript syntax
* Added IntersectionObserver and IntersectionObserverEntry bindings
* Change all `send.pipe` externals to `send`, making them "t-first" (#8)
* Remove deprecated APIs (#16)
* `Window.getSelection` now returns an option, to better match the definition (#15)
* Added `scrollToWithOptions` to `window` (#26)
* Converted to rescript syntax (#18)
* Added `IntersectionObserver` and `IntersectionObserverEntry` bindings (#27)
* Imported `bs-fetch` and converted it to "t-first" (#31)

Todo:
* Convert more input types to `node_like` and `element_like` to improve usability
Expand Down
4 changes: 4 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
MIT License

Copyright (c) 2021 Tiny Technologies Inc (https://www.tiny.cloud/)
Forked from https://github.com/reasonml-community/bs-webapi-incubator:
Copyright (c) 2017 reasonml-community
Forked from https://github.com/reasonml-community/bs-fetch:
Copyright (c) 2017

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
4 changes: 2 additions & 2 deletions bsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"number": "+A-105",
"error": "+A"
},
"bs-dependencies": [
"bs-fetch"
"bs-dev-dependencies": [
"@ryyppy/rescript-promise"
]
}
68 changes: 68 additions & 0 deletions examples/fetch_examples.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
open Webapi
open Promise

let _ = {
Fetch.fetch("/api/hellos/1")
->then(Fetch.Response.text)
->then(text => print_endline(text)->resolve)
}

let _ = {
Fetch.fetchWithInit("/api/hello", Fetch.RequestInit.make(~method_=Post, ()))
->then(Fetch.Response.text)
->then(text => print_endline(text)->resolve)
}

let _ = {
Fetch.fetch("/api/fruit")
/* assume server returns `["apple", "banana", "pear", ...]` */
->then(Fetch.Response.json)
->then(json => Js.Json.decodeArray(json)->resolve)
->then(opt => Belt.Option.getExn(opt)->resolve)
->then(items =>
items->Js.Array2.map(item => item->Js.Json.decodeString->Belt.Option.getExn)->resolve
)
}

/* makes a post request with the following json payload { hello: "world" } */
let _ = {
let payload = Js.Dict.empty()
Js.Dict.set(payload, "hello", Js.Json.string("world"))
Fetch.fetchWithInit(
"/api/hello",
Fetch.RequestInit.make(
~method_=Post,
~body=Fetch.BodyInit.make(Js.Json.stringify(Js.Json.object_(payload))),
~headers=Fetch.HeadersInit.make({"Content-Type": "application/json"}),
(),
),
)->then(Fetch.Response.json)
}

let _ = {
let formData = Webapi.FormData.make()
Webapi.FormData.appendObject(
formData,
"image0",
{"type": "image/jpg", "uri": "path/to/it", "name": "image0.jpg"},
(),
)

Fetch.fetchWithInit(
"/api/upload",
Fetch.RequestInit.make(
~method_=Post,
~body=Fetch.BodyInit.makeWithFormData(formData),
~headers=Fetch.HeadersInit.make({"Accept": "*"}),
(),
),
)->then(Fetch.Response.json)
}

let _ = {
let controller = Fetch.AbortController.make()

let _ = Fetch.fetchWithInit("/api/fruit", Fetch.RequestInit.make(~signal=controller.signal, ()))

controller->Fetch.AbortController.abort
}
48 changes: 48 additions & 0 deletions lib/js/examples/dom_example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';

var Belt_Option = require("rescript/lib/js/belt_Option.js");
var Caml_option = require("rescript/lib/js/caml_option.js");
var Webapi__Dom__Element = require("../src/Webapi/Dom/Webapi__Dom__Element.js");
var Webapi__Dom__Document = require("../src/Webapi/Dom/Webapi__Dom__Document.js");

function unwrapUnsafely(x) {
if (x !== undefined) {
return Caml_option.valFromOption(x);
}
throw {
RE_EXN_ID: "Invalid_argument",
_1: "Passed `None` to unwrapUnsafely",
Error: new Error()
};
}

document.createElement("div").className;

Belt_Option.map(Caml_option.nullable_to_opt(document.createElement("div").nextElementSibling), (function (prim) {
return prim.innerText;
}));

Belt_Option.map(Caml_option.nullable_to_opt(document.createElement("div").parentElement), (function (prim) {
return prim.innerText;
}));

var el = unwrapUnsafely(Webapi__Dom__Element.asHtmlElement(document.createElement("div")));

Belt_Option.map(Belt_Option.flatMap(Webapi__Dom__Document.asHtmlDocument(document), (function (prim) {
return Caml_option.nullable_to_opt(prim.body);
})), (function (body) {
body.appendChild(el);

}));

document.createElement("div").addEventListener("mousemove", (function (e) {
console.log([
e.screenX,
e.screenY
]);

}));

exports.unwrapUnsafely = unwrapUnsafely;
exports.el = el;
/* Not a pure module */
62 changes: 62 additions & 0 deletions lib/js/examples/fetch_examples.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
'use strict';

var Js_json = require("rescript/lib/js/js_json.js");
var Belt_Option = require("rescript/lib/js/belt_Option.js");
var Caml_option = require("rescript/lib/js/caml_option.js");
var Webapi__Fetch = require("../src/Webapi/Webapi__Fetch.js");

fetch("/api/hellos/1").then(function (prim) {
return prim.text();
}).then(function (text) {
return Promise.resolve((console.log(text), undefined));
});

fetch("/api/hello", Webapi__Fetch.RequestInit.make(/* Post */2, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)(undefined)).then(function (prim) {
return prim.text();
}).then(function (text) {
return Promise.resolve((console.log(text), undefined));
});

fetch("/api/fruit").then(function (prim) {
return prim.json();
}).then(function (json) {
return Promise.resolve(Js_json.decodeArray(json));
}).then(function (opt) {
return Promise.resolve(Belt_Option.getExn(opt));
}).then(function (items) {
return Promise.resolve(items.map(function (item) {
return Belt_Option.getExn(Js_json.decodeString(item));
}));
});

var payload = {};

payload["hello"] = "world";

fetch("/api/hello", Webapi__Fetch.RequestInit.make(/* Post */2, {
"Content-Type": "application/json"
}, Caml_option.some(JSON.stringify(payload)), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)(undefined)).then(function (prim) {
return prim.json();
});

var formData = new FormData();

formData.append("image0", {
type: "image/jpg",
uri: "path/to/it",
name: "image0.jpg"
}, undefined);

fetch("/api/upload", Webapi__Fetch.RequestInit.make(/* Post */2, {
Accept: "*"
}, Caml_option.some(formData), undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined)(undefined)).then(function (prim) {
return prim.json();
});

var controller = new AbortController();

fetch("/api/fruit", Webapi__Fetch.RequestInit.make(undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, Caml_option.some(controller.signal))(undefined));

controller.abort();

/* Not a pure module */
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@
"author": "Tiny Technologies Inc",
"license": "MIT",
"devDependencies": {
"@ryyppy/rescript-promise": "^2.1.0",
"rescript": "^9.1.4"
},
"dependencies": {
"bs-fetch": "^0.6.2"
}
}
19 changes: 3 additions & 16 deletions src/Webapi.res
Original file line number Diff line number Diff line change
@@ -1,25 +1,12 @@
module Base64 = Webapi__Base64

/** @since 0.18.0 */
module Blob = Webapi__Blob
module Canvas = Webapi__Canvas
module Dom = Webapi__Dom
module File = Webapi__File
module Fetch = Webapi__Fetch
module FormData = Webapi__FormData

/** Re-export from [bs-fetch] for convenience. To use, you will also
need to add the [bs-fetch] package as a dependency.

To get the [FormData] of an HTML form, use
[Webapi.Dom.HtmlFormElement.data].

@since 0.18.0 */
module FormData = Fetch.FormData

/** Re-export from [bs-fetch] for convenience. See also
{!module:FormData}.

@since 0.18.0 */
module Iterator = FormData.Iterator
module Iterator = Webapi__Iterator

module Performance = Webapi__Performance

Expand Down
2 changes: 1 addition & 1 deletion src/Webapi/Dom/Webapi__Dom__HtmlFormElement.res
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ module Impl = (
@send external checkValidity: t_htmlFormElement => bool = ""
@send external reportValidity: t_htmlFormElement => bool = ""

/** @since 0.18.0 */ @new external data: T.t => Fetch.FormData.t = "FormData"
/** @since 0.18.0 */ @new external data: T.t => Webapi__FormData.t = "FormData"
}

type t = Dom.htmlFormElement
Expand Down
8 changes: 8 additions & 0 deletions src/Webapi/Fetch/Webapi__Fetch__AbortController.res
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
type signal
type t = {signal: signal}

@new external make: unit => t = "AbortController"
@send external abort: t => unit = "abort"

@ocaml.doc("For compatibility. Prefer the record field.")
@get external signal: t => signal = "signal"
2 changes: 1 addition & 1 deletion src/Webapi/Webapi__Blob.res
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module Impl = (
@get external type_: T.t => string = "type"
}

type t = Fetch.blob
type t

include Impl({
type t = t
Expand Down
Loading