diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc070b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/.* +!/.gitignore +/bower_components/ +/node_modules/ +/output/ +/tmp/ diff --git a/README.md b/README.md index 374919b..8726bf6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,655 @@ -# purescript-affjax -An asynchronous AJAX library built using Aff. +# Module Documentation + +## Module Type.Proxy + +#### `Proxy` + +``` purescript +data Proxy a + = Proxy +``` + + + +## Module Network.HTTP.Affjax + +#### `Ajax` + +``` purescript +data Ajax :: ! +``` + +The effect type for AJAX requests made with Affjax. + +#### `Affjax` + +``` purescript +type Affjax e a = Aff (ajax :: Ajax | e) (AffjaxResponse a) +``` + +The type for Affjax requests. + +#### `AffjaxRequest` + +``` purescript +type AffjaxRequest a = { password :: Maybe String, username :: Maybe String, content :: Maybe a, headers :: [RequestHeader], url :: URL, method :: Method } +``` + + +#### `defaultRequest` + +``` purescript +defaultRequest :: AffjaxRequest Unit +``` + + +#### `AffjaxResponse` + +``` purescript +type AffjaxResponse a = { response :: a, headers :: [ResponseHeader], status :: StatusCode } +``` + +The type of records that will be received as an Affjax response. + +#### `URL` + +``` purescript +type URL = String +``` + +Type alias for URL strings to aid readability of types. + +#### `affjax` + +``` purescript +affjax :: forall e a b. (Requestable a, Responsable b) => AffjaxRequest a -> Affjax e b +``` + +Makes an `Affjax` request. + +#### `get` + +``` purescript +get :: forall e a. (Responsable a) => URL -> Affjax e a +``` + +Makes a `GET` request to the specified URL. + +#### `post` + +``` purescript +post :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b +``` + +Makes a `POST` request to the specified URL, sending data. + +#### `post'` + +``` purescript +post' :: forall e a b. (Requestable a, Responsable b) => URL -> Maybe a -> Affjax e b +``` + +Makes a `POST` request to the specified URL with the option to send data. + +#### `post_` + +``` purescript +post_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit +``` + +Makes a `POST` request to the specified URL, sending data and ignoring the +response. + +#### `post_'` + +``` purescript +post_' :: forall e a. (Requestable a) => URL -> Maybe a -> Affjax e Unit +``` + +Makes a `POST` request to the specified URL with the option to send data, +and ignores the response. + +#### `put` + +``` purescript +put :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b +``` + +Makes a `PUT` request to the specified URL, sending data. + +#### `put'` + +``` purescript +put' :: forall e a b. (Requestable a, Responsable b) => URL -> Maybe a -> Affjax e b +``` + +Makes a `PUT` request to the specified URL with the option to send data. + +#### `put_` + +``` purescript +put_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit +``` + +Makes a `PUT` request to the specified URL, sending data and ignoring the +response. + +#### `put_'` + +``` purescript +put_' :: forall e a. (Requestable a) => URL -> Maybe a -> Affjax e Unit +``` + +Makes a `PUT` request to the specified URL with the option to send data, +and ignores the response. + +#### `delete` + +``` purescript +delete :: forall e a. (Responsable a) => URL -> Affjax e a +``` + +Makes a `DELETE` request to the specified URL. + +#### `delete_` + +``` purescript +delete_ :: forall e. URL -> Affjax e Unit +``` + +Makes a `DELETE` request to the specified URL and ignores the response. + +#### `affjax'` + +``` purescript +affjax' :: forall e a b. (Requestable a, Responsable b) => AffjaxRequest a -> (Error -> Eff (ajax :: Ajax | e) Unit) -> (AffjaxResponse b -> Eff (ajax :: Ajax | e) Unit) -> Eff (ajax :: Ajax | e) Unit +``` + +Run a request directly without using `Aff`. + + +## Module Network.HTTP.Method + +#### `Method` + +``` purescript +data Method + = DELETE + | GET + | HEAD + | OPTIONS + | PATCH + | POST + | PUT + | MOVE + | COPY + | CustomMethod String +``` + + +#### `eqMethod` + +``` purescript +instance eqMethod :: Eq Method +``` + + +#### `showMethod` + +``` purescript +instance showMethod :: Show Method +``` + + +#### `methodToString` + +``` purescript +methodToString :: Method -> String +``` + + + +## Module Network.HTTP.MimeType + +#### `MimeType` + +``` purescript +newtype MimeType + = MimeType String +``` + + +#### `eqMimeType` + +``` purescript +instance eqMimeType :: Eq MimeType +``` + + +#### `showMimeType` + +``` purescript +instance showMimeType :: Show MimeType +``` + + +#### `mimeTypeToString` + +``` purescript +mimeTypeToString :: MimeType -> String +``` + + + +## Module Network.HTTP.RequestHeader + +#### `RequestHeader` + +``` purescript +data RequestHeader + = Accept MimeType + | ContentType MimeType + | RequestHeader String String +``` + + +#### `eqRequestHeader` + +``` purescript +instance eqRequestHeader :: Eq RequestHeader +``` + + +#### `showRequestHeader` + +``` purescript +instance showRequestHeader :: Show RequestHeader +``` + + +#### `requestHeaderName` + +``` purescript +requestHeaderName :: RequestHeader -> String +``` + + +#### `requestHeaderValue` + +``` purescript +requestHeaderValue :: RequestHeader -> String +``` + + + +## Module Network.HTTP.ResponseHeader + +#### `ResponseHeader` + +``` purescript +data ResponseHeader +``` + + +#### `responseHeader` + +``` purescript +responseHeader :: String -> String -> ResponseHeader +``` + + +#### `eqResponseHeader` + +``` purescript +instance eqResponseHeader :: Eq ResponseHeader +``` + + +#### `showResponseHeader` + +``` purescript +instance showResponseHeader :: Show ResponseHeader +``` + + + +## Module Network.HTTP.StatusCode + +#### `StatusCode` + +``` purescript +newtype StatusCode + = StatusCode Int +``` + + +#### `eqStatusCode` + +``` purescript +instance eqStatusCode :: Eq StatusCode +``` + + +#### `showStatusCode` + +``` purescript +instance showStatusCode :: Show StatusCode +``` + + + +## Module Network.HTTP.Affjax.Request + +#### `RequestContent` + +``` purescript +data RequestContent :: * +``` + +Type representing all content types that be sent via XHR (ArrayBufferView, +Blob, Document, String, FormData). + +#### `Requestable` + +``` purescript +class Requestable a where + toRequest :: a -> RequestContent +``` + +A class for types that can be converted to values that can be sent with +XHR requests. + +#### `requestableRequestContent` + +``` purescript +instance requestableRequestContent :: Requestable RequestContent +``` + + +#### `requestableInt8Array` + +``` purescript +instance requestableInt8Array :: Requestable (A.ArrayView A.Int8) +``` + + +#### `requestableInt16Array` + +``` purescript +instance requestableInt16Array :: Requestable (A.ArrayView A.Int16) +``` + + +#### `requestableInt32Array` + +``` purescript +instance requestableInt32Array :: Requestable (A.ArrayView A.Int32) +``` + + +#### `requestableUint8Array` + +``` purescript +instance requestableUint8Array :: Requestable (A.ArrayView A.Uint8) +``` + + +#### `requestableUint16Array` + +``` purescript +instance requestableUint16Array :: Requestable (A.ArrayView A.Uint16) +``` + + +#### `requestableUint32Array` + +``` purescript +instance requestableUint32Array :: Requestable (A.ArrayView A.Uint32) +``` + + +#### `requestableUint8ClampedArray` + +``` purescript +instance requestableUint8ClampedArray :: Requestable (A.ArrayView A.Uint8Clamped) +``` + + +#### `requestableFloat32Array` + +``` purescript +instance requestableFloat32Array :: Requestable (A.ArrayView A.Float32) +``` + + +#### `requestableFloat64Array` + +``` purescript +instance requestableFloat64Array :: Requestable (A.ArrayView A.Float64) +``` + + +#### `requestableBlob` + +``` purescript +instance requestableBlob :: Requestable Blob +``` + + +#### `requestableDocument` + +``` purescript +instance requestableDocument :: Requestable Document +``` + + +#### `requestableString` + +``` purescript +instance requestableString :: Requestable String +``` + + +#### `requestableFormData` + +``` purescript +instance requestableFormData :: Requestable FormData +``` + + +#### `requestableUnit` + +``` purescript +instance requestableUnit :: Requestable Unit +``` + + + +## Module Network.HTTP.Affjax.Response + +#### `ResponseContent` + +``` purescript +type ResponseContent = Foreign +``` + +Type representing content types that be received from an XHR request +(ArrayBuffer, Blob, Document, JSON, String). + +#### `Responsable` + +``` purescript +class Responsable a where + responseType :: Proxy a -> ResponseType + fromResponse :: ResponseContent -> F a +``` + + +#### `responsableBlob` + +``` purescript +instance responsableBlob :: Responsable Blob +``` + +#### `responsableDocument` + +``` purescript +instance responsableDocument :: Responsable Document +``` + + +#### `responsableJSON` + +``` purescript +instance responsableJSON :: Responsable Foreign +``` + + +#### `responsableString` + +``` purescript +instance responsableString :: Responsable String +``` + + +#### `responsableUnit` + +``` purescript +instance responsableUnit :: Responsable Unit +``` + + + +## Module Network.HTTP.Affjax.ResponseType + +#### `ResponseType` + +``` purescript +data ResponseType + = ArrayBufferResponse + | BlobResponse + | DocumentResponse + | JSONResponse + | StringResponse +``` + +Valid response types for an AJAX request. This is used to determine the +`ResponseContent` type for a request. + +#### `eqResponseType` + +``` purescript +instance eqResponseType :: Eq ResponseType +``` + + +#### `showResponseType` + +``` purescript +instance showResponseType :: Show ResponseType +``` + + +#### `responseTypeToString` + +``` purescript +responseTypeToString :: ResponseType -> String +``` + + + +## Module Network.HTTP.MimeType.Common + +#### `applicationFormURLEncoded` + +``` purescript +applicationFormURLEncoded :: MimeType +``` + + +#### `applicationJSON` + +``` purescript +applicationJSON :: MimeType +``` + + +#### `applicationJavascript` + +``` purescript +applicationJavascript :: MimeType +``` + + +#### `applicationOctetStream` + +``` purescript +applicationOctetStream :: MimeType +``` + + +#### `applicationXML` + +``` purescript +applicationXML :: MimeType +``` + + +#### `imageGIF` + +``` purescript +imageGIF :: MimeType +``` + + +#### `imageJPEG` + +``` purescript +imageJPEG :: MimeType +``` + + +#### `imagePNG` + +``` purescript +imagePNG :: MimeType +``` + + +#### `multipartFormData` + +``` purescript +multipartFormData :: MimeType +``` + + +#### `textCSV` + +``` purescript +textCSV :: MimeType +``` + + +#### `textHTML` + +``` purescript +textHTML :: MimeType +``` + + +#### `textPlain` + +``` purescript +textPlain :: MimeType +``` + + +#### `textXML` + +``` purescript +textXML :: MimeType +``` + + + + diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..9fbc019 --- /dev/null +++ b/bower.json @@ -0,0 +1,29 @@ +{ + "name": "purescript-affjax", + "homepage": "https://github.com/slamdata/purescript-affjax", + "description": "An asynchronous AJAX library built using Aff.", + "keywords": [ + "purescript", + "ajax" + ], + "license": "Apache 2.0", + "ignore": [ + "**/.*", + "bower_components", + "node_modules", + "output", + "test", + "tmp", + "bower.json", + "gulpfile.js", + "package.json" + ], + "dependencies": { + "purescript-aff": "~0.7.0", + "purescript-arraybuffer-types": "~0.1.1", + "purescript-dom": "~0.1.2", + "purescript-foreign": "~0.4.2", + "purescript-integers": "~0.0.1", + "purescript-nullable": "~0.1.1" + } +} diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..5a883bd --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,34 @@ +"use strict"; + +var gulp = require("gulp"); +var plumber = require("gulp-plumber"); +var purescript = require("gulp-purescript"); +var jsvalidate = require("gulp-jsvalidate"); + +gulp.task("make", function() { + return gulp.src(["src/**/*.purs", "bower_components/purescript-*/src/**/*.purs"]) + .pipe(plumber()) + .pipe(purescript.pscMake()); +}); + +gulp.task("make-test", function() { + return gulp.src(["src/**/*.purs", "test/**/*.purs", "bower_components/purescript-*/src/**/*.purs"]) + .pipe(plumber()) + .pipe(purescript.psc({ main: "Test.Main", output: "test.js" })) + .pipe(gulp.dest("tmp/")); +}); + +gulp.task("jsvalidate", ["make"], function () { + return gulp.src("output/**/*.js") + .pipe(plumber()) + .pipe(jsvalidate()); +}); + +gulp.task("docs", function () { + return gulp.src("src/**/*.purs") + .pipe(plumber()) + .pipe(purescript.pscDocs()) + .pipe(gulp.dest("README.md")); +}); + +gulp.task("default", ["jsvalidate", "docs", "make-test"]); diff --git a/package.json b/package.json new file mode 100644 index 0000000..327f797 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "private": true, + "devDependencies": { + "gulp": "^3.8.11", + "gulp-jsvalidate": "^1.0.1", + "gulp-plumber": "^1.0.0", + "gulp-purescript": "^0.1.2" + } +} diff --git a/src/Network/HTTP/Affjax.purs b/src/Network/HTTP/Affjax.purs new file mode 100644 index 0000000..0558753 --- /dev/null +++ b/src/Network/HTTP/Affjax.purs @@ -0,0 +1,188 @@ +module Network.HTTP.Affjax + ( Ajax() + , Affjax() + , AffjaxRequest(), defaultRequest + , AffjaxResponse() + , URL() + , affjax + , affjax' + , get + , post, post_, post', post_' + , put, put_, put', put_' + , delete, delete_ + ) where + +import Control.Monad.Aff (Aff(), makeAff) +import Control.Monad.Eff (Eff()) +import Control.Monad.Eff.Exception (Error(), error) +import Data.Either (Either(..)) +import Data.Foreign (Foreign(..), F()) +import Data.Function (Fn4(), runFn4) +import Data.Maybe (Maybe(..), maybe) +import Data.Nullable (Nullable(), toNullable) +import Network.HTTP.Affjax.Request +import Network.HTTP.Affjax.Response +import Network.HTTP.Affjax.ResponseType +import Network.HTTP.Method (Method(..), methodToString) +import Network.HTTP.RequestHeader (RequestHeader(), requestHeaderName, requestHeaderValue) +import Network.HTTP.ResponseHeader (ResponseHeader(), responseHeader) +import Network.HTTP.StatusCode (StatusCode()) +import Type.Proxy (Proxy(..)) + +-- | The effect type for AJAX requests made with Affjax. +foreign import data Ajax :: ! + +-- | The type for Affjax requests. +type Affjax e a = Aff (ajax :: Ajax | e) (AffjaxResponse a) + +type AffjaxRequest a = + { method :: Method + , url :: URL + , headers :: [RequestHeader] + , content :: Maybe a + , username :: Maybe String + , password :: Maybe String + } + +defaultRequest :: AffjaxRequest Unit +defaultRequest = + { method: GET + , url: "/" + , headers: [] + , content: Nothing + , username: Nothing + , password: Nothing + } + +-- | The type of records that will be received as an Affjax response. +type AffjaxResponse a = + { status :: StatusCode + , headers :: [ResponseHeader] + , response :: a + } + +-- | Type alias for URL strings to aid readability of types. +type URL = String + +-- | Makes an `Affjax` request. +affjax :: forall e a b. (Requestable a, Responsable b) => AffjaxRequest a -> Affjax e b +affjax = makeAff <<< affjax' + +-- | Makes a `GET` request to the specified URL. +get :: forall e a. (Responsable a) => URL -> Affjax e a +get u = affjax $ defaultRequest { url = u } + +-- | Makes a `POST` request to the specified URL, sending data. +post :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b +post u c = affjax $ defaultRequest { method = POST, url = u, content = Just c } + +-- | Makes a `POST` request to the specified URL with the option to send data. +post' :: forall e a b. (Requestable a, Responsable b) => URL -> Maybe a -> Affjax e b +post' u c = affjax $ defaultRequest { method = POST, url = u, content = c } + +-- | Makes a `POST` request to the specified URL, sending data and ignoring the +-- | response. +post_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit +post_ = post + +-- | Makes a `POST` request to the specified URL with the option to send data, +-- | and ignores the response. +post_' :: forall e a. (Requestable a) => URL -> Maybe a -> Affjax e Unit +post_' = post' + +-- | Makes a `PUT` request to the specified URL, sending data. +put :: forall e a b. (Requestable a, Responsable b) => URL -> a -> Affjax e b +put u c = affjax $ defaultRequest { method = PUT, url = u, content = Just c } + +-- | Makes a `PUT` request to the specified URL with the option to send data. +put' :: forall e a b. (Requestable a, Responsable b) => URL -> Maybe a -> Affjax e b +put' u c = affjax $ defaultRequest { method = PUT, url = u, content = c } + +-- | Makes a `PUT` request to the specified URL, sending data and ignoring the +-- | response. +put_ :: forall e a. (Requestable a) => URL -> a -> Affjax e Unit +put_ = put + +-- | Makes a `PUT` request to the specified URL with the option to send data, +-- | and ignores the response. +put_' :: forall e a. (Requestable a) => URL -> Maybe a -> Affjax e Unit +put_' = put' + +-- | Makes a `DELETE` request to the specified URL. +delete :: forall e a. (Responsable a) => URL -> Affjax e a +delete u = affjax $ defaultRequest { method = DELETE, url = u } + +-- | Makes a `DELETE` request to the specified URL and ignores the response. +delete_ :: forall e. URL -> Affjax e Unit +delete_ = delete + +-- | Run a request directly without using `Aff`. +affjax' :: forall e a b. (Requestable a, Responsable b) => + AffjaxRequest a -> + (Error -> Eff (ajax :: Ajax | e) Unit) -> + (AffjaxResponse b -> Eff (ajax :: Ajax | e) Unit) -> + Eff (ajax :: Ajax | e) Unit +affjax' req eb cb = + runFn4 unsafeAjax responseHeader req' eb cb' + where + req' :: AjaxRequest + req' = { method: methodToString req.method + , url: req.url + , headers: (\h -> { field: requestHeaderName h, value: requestHeaderValue h }) <$> req.headers + , content: toNullable (toRequest <$> req.content) + , responseType: responseTypeToString $ responseType (Proxy :: Proxy b) + , username: toNullable req.username + , password: toNullable req.password + } + cb' :: AffjaxResponse ResponseContent -> Eff (ajax :: Ajax | e) Unit + cb' res = case res { response = _ } <$> fromResponse res.response of + Left err -> eb $ error (show err) + Right res' -> cb res' + +type AjaxRequest = + { method :: String + , url :: URL + , headers :: [{ field :: String, value :: String }] + , content :: Nullable RequestContent + , responseType :: String + , username :: Nullable String + , password :: Nullable String + } + +foreign import unsafeAjax + """ + function unsafeAjax (mkHeader, options, errback, callback) { + return function () { + var xhr = new XMLHttpRequest(); + xhr.open(options.method || "GET", options.url || "/", true, options.username, options.password); + if (options.headers) { + for (var i = 0, header; header = options.headers[i]; i++) { + xhr.setRequestHeader(header.field, header.value); + } + } + xhr.onerror = function (err) { + errback(err)(); + }; + xhr.onload = function () { + callback({ + status: xhr.status, + headers: xhr.getAllResponseHeaders().split("\n") + .filter(function (header) { + return header.length > 0; + }) + .map(function (header) { + var i = header.indexOf(":"); + return mkHeader(header.substring(0, i))(header.substring(i + 2)); + }), + response: xhr.response + })(); + }; + xhr.responseType = options.responseType; + xhr.send(options.content); + }; + } + """ :: forall e a. Fn4 (String -> String -> ResponseHeader) + AjaxRequest + (Error -> Eff (ajax :: Ajax | e) Unit) + (AffjaxResponse Foreign -> Eff (ajax :: Ajax | e) Unit) + (Eff (ajax :: Ajax | e) Unit) diff --git a/src/Network/HTTP/Affjax/Request.purs b/src/Network/HTTP/Affjax/Request.purs new file mode 100644 index 0000000..49cdc66 --- /dev/null +++ b/src/Network/HTTP/Affjax/Request.purs @@ -0,0 +1,70 @@ +module Network.HTTP.Affjax.Request + ( RequestContent() + , Requestable, toRequest + ) where + +import DOM (Document()) +import DOM.File (Blob()) +import DOM.XHR (FormData()) +import qualified Data.ArrayBuffer.Types as A + +-- | Type representing all content types that be sent via XHR (ArrayBufferView, +-- | Blob, Document, String, FormData). +foreign import data RequestContent :: * + +-- | A class for types that can be converted to values that can be sent with +-- | XHR requests. +class Requestable a where + toRequest :: a -> RequestContent + +instance requestableRequestContent :: Requestable RequestContent where + toRequest = id + +instance requestableInt8Array :: Requestable (A.ArrayView A.Int8) where + toRequest = unsafeConversion + +instance requestableInt16Array :: Requestable (A.ArrayView A.Int16) where + toRequest = unsafeConversion + +instance requestableInt32Array :: Requestable (A.ArrayView A.Int32) where + toRequest = unsafeConversion + +instance requestableUint8Array :: Requestable (A.ArrayView A.Uint8) where + toRequest = unsafeConversion + +instance requestableUint16Array :: Requestable (A.ArrayView A.Uint16) where + toRequest = unsafeConversion + +instance requestableUint32Array :: Requestable (A.ArrayView A.Uint32) where + toRequest = unsafeConversion + +instance requestableUint8ClampedArray :: Requestable (A.ArrayView A.Uint8Clamped) where + toRequest = unsafeConversion + +instance requestableFloat32Array :: Requestable (A.ArrayView A.Float32) where + toRequest = unsafeConversion + +instance requestableFloat64Array :: Requestable (A.ArrayView A.Float64) where + toRequest = unsafeConversion + +instance requestableBlob :: Requestable Blob where + toRequest = unsafeConversion + +instance requestableDocument :: Requestable Document where + toRequest = unsafeConversion + +instance requestableString :: Requestable String where + toRequest = unsafeConversion + +instance requestableFormData :: Requestable FormData where + toRequest = unsafeConversion + +instance requestableUnit :: Requestable Unit where + toRequest = unsafeConversion + +foreign import unsafeConversion + """ + function unsafeConversion (x) { + return x; + } + """ :: forall a b. a -> b diff --git a/src/Network/HTTP/Affjax/Response.purs b/src/Network/HTTP/Affjax/Response.purs new file mode 100644 index 0000000..3fd80c2 --- /dev/null +++ b/src/Network/HTTP/Affjax/Response.purs @@ -0,0 +1,69 @@ +module Network.HTTP.Affjax.Response + ( ResponseContent() + , Responsable, responseType, fromResponse + ) where + +import Control.Bind ((>=>)) +import Data.Either (Either(..)) +import Data.Foreign (Foreign(), F(), readString, parseJSON, unsafeReadTagged) +import DOM (Document()) +import DOM.File (Blob()) +import DOM.XHR (FormData()) +import Network.HTTP.Affjax.ResponseType +import Type.Proxy (Proxy()) +import qualified Data.ArrayBuffer.Types as A + +-- | Type representing content types that be received from an XHR request +-- | (ArrayBuffer, Blob, Document, JSON, String). +type ResponseContent = Foreign + +class Responsable a where + responseType :: Proxy a -> ResponseType + fromResponse :: ResponseContent -> F a + +-- rInt8Array :: Responsable A.Int8Array +-- rInt8Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +-- rInt16Array :: Responsable A.Int16Array +-- rInt16Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +-- rInt32Array :: Responsable A.Int32Array +-- rInt32Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +-- rUint8Array :: Responsable A.Uint8Array +-- rUint8Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +-- rUint16Array :: Responsable A.Uint16Array +-- rUint16Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +-- rUint32Array :: Responsable A.Uint32Array +-- rUint32Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +-- rUint8ClampedArray :: Responsable A.Uint8ClampedArray +-- rUint8ClampedArray = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +-- rFloat32Array :: Responsable A.Float32Array +-- rFloat32Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +-- rFloat64Array :: Responsable A.Float64Array +-- rFloat64Array = Responsable (unsafeReadTagged "ArrayBuffer") ArrayBufferResponse + +instance responsableBlob :: Responsable Blob where + responseType _ = BlobResponse + fromResponse = unsafeReadTagged "Blob" + +instance responsableDocument :: Responsable Document where + responseType _ = DocumentResponse + fromResponse = unsafeReadTagged "Document" + +instance responsableJSON :: Responsable Foreign where + responseType _ = JSONResponse + fromResponse = Right + +instance responsableString :: Responsable String where + responseType _ = StringResponse + fromResponse = readString + +instance responsableUnit :: Responsable Unit where + responseType _ = StringResponse + fromResponse = const (Right unit) diff --git a/src/Network/HTTP/Affjax/ResponseType.purs b/src/Network/HTTP/Affjax/ResponseType.purs new file mode 100644 index 0000000..71f7373 --- /dev/null +++ b/src/Network/HTTP/Affjax/ResponseType.purs @@ -0,0 +1,33 @@ +module Network.HTTP.Affjax.ResponseType where + +-- | Valid response types for an AJAX request. This is used to determine the +-- | `ResponseContent` type for a request. +data ResponseType + = ArrayBufferResponse + | BlobResponse + | DocumentResponse + | JSONResponse + | StringResponse + +instance eqResponseType :: Eq ResponseType where + (==) ArrayBufferResponse ArrayBufferResponse = true + (==) BlobResponse BlobResponse = true + (==) DocumentResponse DocumentResponse = true + (==) JSONResponse JSONResponse = true + (==) StringResponse StringResponse = true + (==) _ _ = false + (/=) x y = not (x == y) + +instance showResponseType :: Show ResponseType where + show ArrayBufferResponse = "ArrayBufferResponse" + show BlobResponse = "BlobResponse" + show DocumentResponse = "DocumentResponse" + show JSONResponse = "JSONResponse" + show StringResponse = "StringResponse" + +responseTypeToString :: ResponseType -> String +responseTypeToString ArrayBufferResponse = "arraybuffer" +responseTypeToString BlobResponse = "blob" +responseTypeToString DocumentResponse = "document" +responseTypeToString JSONResponse = "json" +responseTypeToString StringResponse = "text" diff --git a/src/Network/HTTP/Method.purs b/src/Network/HTTP/Method.purs new file mode 100644 index 0000000..6f6a469 --- /dev/null +++ b/src/Network/HTTP/Method.purs @@ -0,0 +1,42 @@ +module Network.HTTP.Method where + +data Method + = DELETE + | GET + | HEAD + | OPTIONS + | PATCH + | POST + | PUT + | MOVE + | COPY + | CustomMethod String + +instance eqMethod :: Eq Method where + (==) DELETE DELETE = true + (==) GET GET = true + (==) HEAD HEAD = true + (==) OPTIONS OPTIONS = true + (==) PATCH PATCH = true + (==) POST POST = true + (==) PUT PUT = true + (==) MOVE MOVE = true + (==) COPY COPY = true + (==) _ _ = false + (/=) x y = not (x == y) + +instance showMethod :: Show Method where + show DELETE = "DELETE" + show GET = "GET" + show HEAD = "HEAD" + show OPTIONS = "OPTIONS" + show PATCH = "PATCH" + show POST = "POST" + show PUT = "PUT" + show MOVE = "MOVE" + show COPY = "COPY" + show (CustomMethod m) = "(CustomMethod " ++ show m ++ ")" + +methodToString :: Method -> String +methodToString (CustomMethod m) = m +methodToString other = show other diff --git a/src/Network/HTTP/MimeType.purs b/src/Network/HTTP/MimeType.purs new file mode 100644 index 0000000..5e8acc2 --- /dev/null +++ b/src/Network/HTTP/MimeType.purs @@ -0,0 +1,13 @@ +module Network.HTTP.MimeType where + +newtype MimeType = MimeType String + +instance eqMimeType :: Eq MimeType where + (==) (MimeType x) (MimeType y) = x == y + (/=) (MimeType x) (MimeType y) = x /= y + +instance showMimeType :: Show MimeType where + show (MimeType h) = "(MimeType " ++ show h ++ ")" + +mimeTypeToString :: MimeType -> String +mimeTypeToString (MimeType s) = s diff --git a/src/Network/HTTP/MimeType/Common.purs b/src/Network/HTTP/MimeType/Common.purs new file mode 100644 index 0000000..976f328 --- /dev/null +++ b/src/Network/HTTP/MimeType/Common.purs @@ -0,0 +1,42 @@ +module Network.HTTP.MimeType.Common where + +import Network.HTTP.MimeType + +applicationFormURLEncoded :: MimeType +applicationFormURLEncoded = MimeType "application/x-www-form-urlencoded" + +applicationJSON :: MimeType +applicationJSON = MimeType "application/json" + +applicationJavascript :: MimeType +applicationJavascript = MimeType "application/javascript" + +applicationOctetStream :: MimeType +applicationOctetStream = MimeType "application/octet-stream" + +applicationXML :: MimeType +applicationXML = MimeType "application/xml" + +imageGIF :: MimeType +imageGIF = MimeType "image/gif" + +imageJPEG :: MimeType +imageJPEG = MimeType "image/jpeg" + +imagePNG :: MimeType +imagePNG = MimeType "image/png" + +multipartFormData :: MimeType +multipartFormData = MimeType "multipart/form-data" + +textCSV :: MimeType +textCSV = MimeType "text/csv" + +textHTML :: MimeType +textHTML = MimeType "text/html" + +textPlain :: MimeType +textPlain = MimeType "text/plain" + +textXML :: MimeType +textXML = MimeType "text/xml" diff --git a/src/Network/HTTP/RequestHeader.purs b/src/Network/HTTP/RequestHeader.purs new file mode 100644 index 0000000..871d7d4 --- /dev/null +++ b/src/Network/HTTP/RequestHeader.purs @@ -0,0 +1,30 @@ +module Network.HTTP.RequestHeader where + +import Network.HTTP.MimeType + +data RequestHeader + = Accept MimeType + | ContentType MimeType + | RequestHeader String String + +instance eqRequestHeader :: Eq RequestHeader where + (==) (Accept m1) (Accept m2) = m1 == m2 + (==) (ContentType m1) (ContentType m2) = m1 == m2 + (==) (RequestHeader h1 v1) (RequestHeader h2 v2) = h1 == h2 && v1 == v2 + (==) _ _ = false + (/=) x y = not (x == y) + +instance showRequestHeader :: Show RequestHeader where + show (Accept m) = "(Accept " ++ show m ++ ")" + show (ContentType m) = "(ContentType " ++ show m ++ ")" + show (RequestHeader h v) = "(RequestHeader " ++ show h ++ " " ++ show v ++ ")" + +requestHeaderName :: RequestHeader -> String +requestHeaderName (Accept _) = "Accept" +requestHeaderName (ContentType _) = "Content-Type" +requestHeaderName (RequestHeader h _) = h + +requestHeaderValue :: RequestHeader -> String +requestHeaderValue (Accept m) = mimeTypeToString m +requestHeaderValue (ContentType m) = mimeTypeToString m +requestHeaderValue (RequestHeader _ v) = v diff --git a/src/Network/HTTP/ResponseHeader.purs b/src/Network/HTTP/ResponseHeader.purs new file mode 100644 index 0000000..80af915 --- /dev/null +++ b/src/Network/HTTP/ResponseHeader.purs @@ -0,0 +1,17 @@ +module Network.HTTP.ResponseHeader + ( ResponseHeader() + , responseHeader + ) where + +data ResponseHeader = ResponseHeader String String + +responseHeader :: String -> String -> ResponseHeader +responseHeader field value = ResponseHeader field value + +instance eqResponseHeader :: Eq ResponseHeader where + (==) (ResponseHeader h1 v1) (ResponseHeader h2 v2) = h1 == h2 && v1 == v2 + (==) _ _ = false + (/=) x y = not (x == y) + +instance showResponseHeader :: Show ResponseHeader where + show (ResponseHeader h v) = "(ResponseHeader " ++ show h ++ " " ++ show v ++ ")" diff --git a/src/Network/HTTP/StatusCode.purs b/src/Network/HTTP/StatusCode.purs new file mode 100644 index 0000000..93b67c0 --- /dev/null +++ b/src/Network/HTTP/StatusCode.purs @@ -0,0 +1,12 @@ +module Network.HTTP.StatusCode where + +import Data.Int + +newtype StatusCode = StatusCode Int + +instance eqStatusCode :: Eq StatusCode where + (==) (StatusCode x) (StatusCode y) = x == y + (/=) x y = not (x == y) + +instance showStatusCode :: Show StatusCode where + show (StatusCode code) = "(StatusCode " ++ show code ++ ")" diff --git a/src/Type/Proxy.purs b/src/Type/Proxy.purs new file mode 100644 index 0000000..ee3ccda --- /dev/null +++ b/src/Type/Proxy.purs @@ -0,0 +1,3 @@ +module Type.Proxy where + +data Proxy a = Proxy diff --git a/test/Main.purs b/test/Main.purs new file mode 100644 index 0000000..9873f68 --- /dev/null +++ b/test/Main.purs @@ -0,0 +1,36 @@ +module Test.Main where + +import Control.Monad.Aff +import Control.Monad.Eff +import Control.Monad.Eff.Class +import Control.Monad.Eff.Exception +import Data.Either +import Data.Foreign +import Debug.Trace +import Network.HTTP.Affjax +import Network.HTTP.Affjax.Response +import Network.HTTP.Affjax.Request +import Network.HTTP.Method +import Network.HTTP.MimeType.Common +import Network.HTTP.RequestHeader + +foreign import traceAny + """ + function traceAny(a){ + return function () { + console.log(a); + return {}; + }; + } + """ :: forall e a. a -> Eff (trace :: Trace | e) Unit + +main = launchAff $ do + + res <- attempt $ affjax $ defaultRequest { url = "/api", method = POST } + liftEff $ either traceAny (traceAny :: AffjaxResponse String -> _) res + + res <- attempt $ post_ "/api" "test" + liftEff $ either traceAny traceAny res + + res <- attempt $ get "/arrayview" + liftEff $ either traceAny (traceAny :: AffjaxResponse Foreign -> _) res