From d014c5adab43703646304bd03280f7664a3e3f84 Mon Sep 17 00:00:00 2001 From: Rashad Gover Date: Thu, 20 Apr 2023 02:52:08 -0500 Subject: [PATCH] Update README.md --- README.md | 210 ------------------------------------------------------ 1 file changed, 210 deletions(-) diff --git a/README.md b/README.md index 5d60a46..b659ed0 100644 --- a/README.md +++ b/README.md @@ -107,213 +107,3 @@ with Endpoints, but not a server implemented with Matchpoints. On the flip side, a server implemented with Matchpoints will be more concise than a server implemented with Endpoints. - -## Getting Started - -1. Use the command `stack new ` to create a new Haskell project -2. Add the `okapi` library to your project's dependencies - -## Endpoint - -An *Endpoint* is an *executable specification* that represents a single endpoint of -your API. - -An Endpoint has 6 fields: - -```haskell -data Endpoint p q h b r = Endpoint - { method :: StdMethod, -- (1) - pathScript :: Path.Script p, -- (2) - queryScript :: Query.Script q, -- (3) - headersScript :: Headers.Script h, -- (4) - bodyScript :: Body.Script b, -- (5) - responderScript :: Responder.Script r -- (6) - } -``` - -The `method` field is a simple value, but the other fields point to a `Script` that represents their respective HTTP request parts. - -The type parameter of a Script represents the type of value it returns. - -Therefore, the concrete type of an Endpoint is determined by the return types of -the Scripts that are used to construct the Endpoint. - -All the different Script types are Applicatives, but not all are *lawful applicatives*. - -Since all Script types are Applicatives, we can use the Applicative typeclass methods to write our scripts. Here's an example of a query Script: - -```haskell -data Filter = Filter - { color :: Text - , categoryID :: Int - , isOnSale :: Maybe () - } - -myQueryScript :: Query.Script Filter -myQueryScript = Filter - <$> Query.param "color" - <*> Query.param "category" - <*> (Query.optional $ Query.flag "sale") -``` - -If you have the `-XApplicativeDo` language extension turned on, you can also write your Scripts using `do` syntax. - -We recommend using `-XApplicativeDo` in conjuction with the `-XRecordWildCards` language extension if you're not comfortable with using the Applicative operators. Here's the same query script we defined above, but with these language extensions turned on: - -```haskell -{-# LANGUAGE ApplicativeDo #-} -{-# LANGUAGE RecordWildCards #-} - -myQueryScript :: Query.Script Filter -myQueryScript = do - color <- Query.param "color" - categoryID <- Query.param "category" - isOnSale <- Query.optional $ Query.flag "sale" - pure Filter {..} -``` - -Each Script type has its' own operations suited to parsing its' respective part of the -request. These are defined in more detail below. - -1. ### Method - - The `method` field represents the HTTP method that the Endpoint accepts. - - Its' type is `StdMethod` from the `http-types` library. - - Only the standard methods mentioned in RFC-1234 are allowed. - -2. ### Path Script - - The `pathScript` field defines the request path that the Endpoint accepts, including *path parameters*. - - Path Scripts have two operations: `static` and `param`. - - ```haskell - myPathScript = Path.static "person" *> Path.param @Int "personID" - ``` - -3. ### Query Script - - The `queryScript` field defines the query that the Endpoint accepts. - - There are two operations for Query Scripts: `param` and `flag`. - - There are two modifiers for Query Scripts: `optional` and `option`. - - `optional` and `option` are specialized versions of the `optional` and `option` - parser combinators found in the `parser-combinators` library. - - ```haskell - myQueryScript :: Query.Script (Text, Maybe Float, Int) - myQueryScript = do - lastName <- Query.param "last_name" - optSalary <- Query.optional $ Query.param "salary" - minAge <- Query.option 21 $ Query.param "min_age" - pure (lastName, optSalary, minAge) - ``` - -4. ### Body Script - - The `bodyScript` field defines the request body and it's content type that the Endpoint accepts. - - There are four operations for Body Scripts: `json`, `form`, `formParam`, `file`. - - There are two modifiers for Body Scripts: `optional` and `option`. - - ```haskell - myBodyScript :: Body.Script (Maybe Value) - myBodyScript = Body.optional $ Body.json @Value - ``` - -5. ### Headers Script - - The `headersScript` field defines the request headers that the Endpoint accepts. - - There are two operations for Headers Scripts: `param` and `cookie`. - - There are two modifiers for Headers Scripts: `optional` and `option`. - - ```haskell - myHeadersScript :: Headers.Script _ - myHeadersScript = undefined - ``` - -6. ### Responder Script - - The `responderScript` field defines the responses that the Endpoint's handler MUST return. - - Responder Scripts have to be more complex than the other Script types in order for the Endpoint to have a contract with the handler that the handler will respond with the responses defined in the Responder Script. - - This is done using a combination of higher order functions, linear types, and smart constructors. - - Responder Script operations have to take a *Responder Headers* Script as an argument to define what headers will be attached to the Response. - - For now, there is only one operation for Responder Scripts: `json`. - - Responder Header Scripts also only have one operation: `has`. - - ```haskell - {-# LANGUAGE ApplicativeDo #-} - {-# LANGUAGE RecordWildCards #-} - {-# LANGUAGE BlockArguments #-} - - data SecretHeaders = SecretHeaders - { firstSecret :: Int -> Response -> Response - , secondSecret :: Int -> Response -> Response - } - - data MyResponders = MyResponders - { allGood :: (SecretHeaders %1 -> Response -> Response) -> Text -> Response - , notGood :: (() %1 -> Response -> Response) -> Text -> Response - } - - myResponderScript = do - allGood <- Responder.json @Text status200 do - addSecret <- ResponderHeaders.has @Int "IntSecret" - addAnotherSecret <- ResponderHeaders.has @Int "X-Another-Secret" - pure SecretHeaders {..} - notGood <- Responder.json @Text status501 $ pure () - pure MyResponders {..} - - myHandler someNumber _ _ _ _ (MyResponders allGood notGood) = do - if someNumber < 100 - then do - return $ allGood - (\(SecretHeaders firstSecret secondSecret) response -> secondSecret 0 $ firstSecret 7 response) - "All Good!" - else do - return $ notGood - (\() response -> response) - "Not Good!" - ``` - - More information about *Responders* and *ResponderHeaders* is available in the Handler section. - -## Handler - -Handlers are simple: they are contextful functions from the arguments provided by an Endpoint, to a Response. - -The type synonym `Handler` represents the type of Handlers in Okapi: - -```haskell -type Handler m p q b h r = p -> q -> b -> h -> r -> m Response -``` - -The type parameter `m` represents the context in which the Handler creates the Response. - -The type parameters `p`, `q`, `b`, `h` and `r` represent the types of the values returned by the Endpoint's Path, Query, Body, Headers and Responder Scripts respectively. - -## Plan - -A Plan - -## Server - -### Lifter - -### Execution - -## Matchpoint - -## TLDR