Skip to content

Commit

Permalink
Add rudimentary docs
Browse files Browse the repository at this point in the history
  • Loading branch information
robashton committed Jul 6, 2019
1 parent 4a168b5 commit caa9dd5
Show file tree
Hide file tree
Showing 6 changed files with 463 additions and 4 deletions.
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ PS_ERL_FFI = $(shell find ${PS_SRC} -type f -name \*.erl)
PACKAGE_SET = $(shell jq '.set' < psc-package.json)
ERL_MODULES_VERSION = $(shell jq '."erl-modules".version' < .psc-package/$(PACKAGE_SET)/.set/packages.json)

all: output
all: output docs

output: $(PS_SOURCEFILES) $(PS_ERL_FFI) .psc-package
.psc-package/${PACKAGE_SET}/erl-modules/${ERL_MODULES_VERSION}/scripts/gen_module_names.sh src/Stetson Stetson.ModuleNames
psc-package sources | xargs purs compile '$(PS_SRC)/**/*.purs'
@touch output

docs: $(PS_SOURCEFILES) $(PS_ERL_FFI) .psc-package
mkdir -p docs
psc-package sources | xargs purs docs '$(PS_SRC)/**/*.purs' \
--docgen Stetson:docs/Stetson.md \
--docgen Stetson.Rest:docs/Stetson.Rest.md
touch docs

.psc-package: psc-package.json
psc-package install
touch .psc-package
Expand Down
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# purescript-erl-stetson

Opinionated Bindings to Cowboy

## Type-safe bindings

Direct bindings to Cowboy's API exist in the package 'erl-cowboy', but in practise this are unwieldy to build an application with - this module seeks to provide a more functional set of behaviours around the low level bindings - more sspecifically, the more REST oriented side of things.

So for example, a complete web server serving some static content would look like this

## A complete web server

```purescript
Stetson.configure -- Start configuring Stetson
# Stetson.route "/" routeHandler -- Define a route, invoke 'routeHandler' to find out about it
# Stetson.port 8080 -- Listen on port 8080
# Stetson.bindTo 0 0 0 0 -- And all interfaces
# Stetson.startClear "http_listener" -- Start the listener
-- Our route handler..
routeHandler :: StetsonHandler Unit -- Has a type of 'Unit' - this is the state that gets passed around to all callbacks
routeHandler =
Rest.handler (\req -> Rest.initResult req unit) -- Callback invoked on init, define the initial state (in this case, 'unit')
# Rest.contentTypesProvided (\req state -> Rest.result (tuple2 "text/html" writeText : nil) req state) -- Callback to provide list of handlers for different content types
# Rest.yeeha -- Finish defining the handler
where
writeText req state = do -- Callback invoked when we want to serve text/html as above
Rest.result "Hello World" req state) -- The result of providing text/html, Hello World
```

An actual example with more context can be found in the [demo project](https://github.com/id3as/demo-ps)

127 changes: 127 additions & 0 deletions docs/Stetson.Rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
## Module Stetson.Rest

This module contains the functions necessary to define a rest handler for a route in Stetson/Cowboy
This maps pretty much 1-1 onto https://ninenines.eu/docs/en/cowboy/2.5/guide/rest_handlers/#_callbacks
Although only the handlers that we have needed so far are defined - feel free to send pull requests that add the ones you need

#### `handler`

``` purescript
handler :: forall state. InitHandler state -> RestHandler state
```

Create a cowboy REST handler with the provided Init handler and no callbacks defined

#### `allowedMethods`

``` purescript
allowedMethods :: forall state. (Req -> state -> Effect (RestResult (List HttpMethod) state)) -> RestHandler state -> RestHandler state
```

Add an allowedMethods callback to the provided RestHandler

#### `resourceExists`

``` purescript
resourceExists :: forall state. (Req -> state -> Effect (RestResult Boolean state)) -> RestHandler state -> RestHandler state
```

Add a resourceExists callback to the provided RestHandler

#### `isAuthorized`

``` purescript
isAuthorized :: forall state. (Req -> state -> Effect (RestResult Authorized state)) -> RestHandler state -> RestHandler state
```

Add an isAuthorized callback to the provided RestHandler

#### `contentTypesAccepted`

``` purescript
contentTypesAccepted :: forall state. (Req -> state -> Effect (RestResult (List (Tuple2 String (AcceptHandler state))) state)) -> RestHandler state -> RestHandler state
```

Add a contentTypesAccepted callback to the provided RestHandler

#### `contentTypesProvided`

``` purescript
contentTypesProvided :: forall state. (Req -> state -> Effect (RestResult (List (Tuple2 String (ProvideHandler state))) state)) -> RestHandler state -> RestHandler state
```

Add a contentTypesProvided callback to the provided RestHandler

#### `deleteResource`

``` purescript
deleteResource :: forall state. (Req -> state -> Effect (RestResult Boolean state)) -> RestHandler state -> RestHandler state
```

Add a deleteResource callback to the provided RestHandler

#### `movedTemporarily`

``` purescript
movedTemporarily :: forall state. (Req -> state -> Effect (RestResult MovedResult state)) -> RestHandler state -> RestHandler state
```

Add a movedTemporarily callback to the provided RestHandler

#### `movedPermanently`

``` purescript
movedPermanently :: forall state. (Req -> state -> Effect (RestResult MovedResult state)) -> RestHandler state -> RestHandler state
```

Add a movedPermanently callback to the provided RestHandler

#### `serviceAvailable`

``` purescript
serviceAvailable :: forall state. (Req -> state -> Effect (RestResult Boolean state)) -> RestHandler state -> RestHandler state
```

Add a serviceAvailable callback to the provided RestHandler

#### `previouslyExisted`

``` purescript
previouslyExisted :: forall state. (Req -> state -> Effect (RestResult Boolean state)) -> RestHandler state -> RestHandler state
```

Add a previouslyExisted callback to the provided RestHandler

#### `forbidden`

``` purescript
forbidden :: forall state. (Req -> state -> Effect (RestResult Boolean state)) -> RestHandler state -> RestHandler state
```

Add a forbidden callback to the provided RestHandler

#### `initResult`

``` purescript
initResult :: forall state. Req -> state -> Effect (InitResult state)
```

Create an init response for return from an InitHandler

#### `result`

``` purescript
result :: forall reply state. reply -> Req -> state -> Effect (RestResult reply state)
```

Create an rest response for return from a rest callback

#### `yeeha`

``` purescript
yeeha :: forall state. RestHandler state -> StetsonHandler state
```

Finish defining this rest handler, yeehaaw


205 changes: 205 additions & 0 deletions docs/Stetson.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
## Module Stetson

This is the entry point into the Stetson wrapper
You'll want to call Stetson.configure and then follow the types..

#### `RestResult`

``` purescript
data RestResult reply state
= RestOk reply Req state
```

The return type of most of the callbacks invoked as part of the REST workflow

#### `InitResult`

``` purescript
data InitResult state
= InitOk Req state
```

The return type of the 'init' callback in the REST workflow

#### `InitHandler`

``` purescript
type InitHandler state = Req -> Effect (InitResult state)
```

The callback invoked to kick off the REST workflow

#### `AcceptHandler`

``` purescript
type AcceptHandler state = Req -> state -> Effect (RestResult Boolean state)
```

A callback invoked to 'accept' a specific content type

#### `ProvideHandler`

``` purescript
type ProvideHandler state = Req -> state -> Effect (RestResult String state)
```

A callback invoked to 'provide' a specific content type

#### `RestHandler`

``` purescript
type RestHandler state = { init :: Req -> Effect (InitResult state), allowedMethods :: Maybe (Req -> state -> Effect (RestResult (List HttpMethod) state)), resourceExists :: Maybe (Req -> state -> Effect (RestResult Boolean state)), contentTypesAccepted :: Maybe (Req -> state -> Effect (RestResult (List (Tuple2 String (AcceptHandler state))) state)), contentTypesProvided :: Maybe (Req -> state -> Effect (RestResult (List (Tuple2 String (ProvideHandler state))) state)), deleteResource :: Maybe (Req -> state -> Effect (RestResult Boolean state)), isAuthorized :: Maybe (Req -> state -> Effect (RestResult Authorized state)), movedTemporarily :: Maybe (Req -> state -> Effect (RestResult MovedResult state)), movedPermanently :: Maybe (Req -> state -> Effect (RestResult MovedResult state)), serviceAvailable :: Maybe (Req -> state -> Effect (RestResult Boolean state)), previouslyExisted :: Maybe (Req -> state -> Effect (RestResult Boolean state)), forbidden :: Maybe (Req -> state -> Effect (RestResult Boolean state)) }
```

A builder containing the complete set of callbacks during the rest workflow for a specific handler

#### `HttpMethod`

``` purescript
data HttpMethod
= GET
| POST
| HEAD
| OPTIONS
| PUT
| DELETE
```

or is it a verb

##### Instances
``` purescript
Show HttpMethod
```

#### `Authorized`

``` purescript
data Authorized
= Authorized
| NotAuthorized String
```

Return type of the isAuthorized callback

#### `StetsonHandler`

``` purescript
data StetsonHandler state
= Rest (RestHandler state)
```

#### `StaticAssetLocation`

``` purescript
data StaticAssetLocation
= PrivDir String String
| PrivFile String String
```

#### `StetsonRoute`

``` purescript
type StetsonRoute = { route :: String, moduleName :: NativeModuleName, args :: HandlerArgs }
```

#### `HandlerArgs`

``` purescript
data HandlerArgs :: Type
```

#### `ConfiguredRoute`

``` purescript
data ConfiguredRoute
= Stetson StetsonRoute
| Cowboy Path
```

#### `StetsonConfig`

``` purescript
type StetsonConfig = { bindPort :: Int, bindAddress :: Tuple4 Int Int Int Int, streamHandlers :: Maybe (List NativeModuleName), middlewares :: Maybe (List NativeModuleName), routes :: List ConfiguredRoute }
```

#### `configure`

``` purescript
configure :: StetsonConfig
```

Creates a blank stetson config with default settings and no routes

#### `route`

``` purescript
route :: forall state. String -> StetsonHandler state -> StetsonConfig -> StetsonConfig
```

Add a route to a StetsonConfig
value: The path this route will handle (this takes the same format as cowboy routes)
handler: The handler that will take care of this request
config: The config to add this route to
```purescript
let newConfig = Stetson.route "/items/:id" myHandler config
```

#### `static`

``` purescript
static :: String -> StaticAssetLocation -> StetsonConfig -> StetsonConfig
```

Add a static route handler to a StetsonConfig
This can either be a file or a directory to serve a file or files from

#### `cowboyRoutes`

``` purescript
cowboyRoutes :: List Path -> StetsonConfig -> StetsonConfig
```

Introduce a list of native Erlang cowboy handlers to this config

#### `port`

``` purescript
port :: Int -> StetsonConfig -> StetsonConfig
```

Set the port that this http listener will listen to

#### `bindTo`

``` purescript
bindTo :: Int -> Int -> Int -> Int -> StetsonConfig -> StetsonConfig
```

Set the IP that this http listener will bind to (default: 0.0.0.0)

#### `streamHandlers`

``` purescript
streamHandlers :: List NativeModuleName -> StetsonConfig -> StetsonConfig
```

Supply a list of modules to act as native stream handlers in cowboy

#### `middlewares`

``` purescript
middlewares :: List NativeModuleName -> StetsonConfig -> StetsonConfig
```

Supply a list of modules to act as native middlewares in cowboy

#### `startClear`

``` purescript
startClear :: String -> StetsonConfig -> Effect Unit
```

Start the listener with the specified name


Loading

0 comments on commit caa9dd5

Please sign in to comment.