packages feed

erpnext-api-client 0.1.0.0 → 0.1.0.1

raw patch · 7 files changed

+75/−29 lines, 7 filessetup-changedPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

Files

README.md view
@@ -61,7 +61,7 @@   manager <- newManager defaultManagerSettings   response <- getDocType manager config "Company A" :: IO (ApiResponse Customer)   case response of-    Ok _ c _ -> putStrLn $ name c+    Ok _ _ c -> putStrLn $ name c     Err r _ -> print r ``` @@ -103,9 +103,23 @@ dependency. The resulting record types can be used together with this API client but the `IsDocType` instance must still be defined by hand. -****The API client part generated from the OpenAPI spec can not be-used.****+**The API client part generated from the OpenAPI spec can not be used.** +Example models file:++``` plain+SalesOrder+  name Text+  total Double+  transaction_date Text+  items [SalesOrderItem]+  Required name++SalesOrderItem+  name Text+  Required name+```+ ``` example $ ./scripts/gen-openapi-yaml.sh models > openapi.yaml $ openapi3-code-generator-exe \@@ -125,13 +139,11 @@ │   │   ├── SecuritySchemes.hs │   │   ├── TypeAlias.hs │   │   ├── Types     <---- here are the generated types-│   │   │   ├── Customer.hs-│   │   │   ├── Order.hs-│   │   │   ├── Supplier.hs+│   │   │   ├── SalesOrder.hs+│   │   │   ├── SalesOrderItem.hs │   │   └── Types.hs │   └── ERPNextAPI.hs-├── stack.yaml-└── stack.yaml.lock+└── stack.yaml ```  ## Note on TLS problems
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
erpnext-api-client.cabal view
@@ -1,7 +1,7 @@ cabal-version:       3.6  name:                erpnext-api-client-version:             0.1.0.0+version:             0.1.0.1 synopsis:            Generic API client library for ERPNext description:         This is a Haskell API client for ERPNext. It aims to be a light-weight library based on http-client and user-provided record types. homepage:            https://github.com/schoettl/erpnext-api-client
src/ERPNext/Client.hs view
@@ -2,6 +2,17 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE OverloadedStrings #-} +{-|+Description: Generic API client library for ERPNext++This is a Haskell API client for ERPNext. It aims to be a light-weight+library based on http-client and user-provided record types.++API documentation:++https://docs.frappe.io/framework/user/en/api/rest+-}+ module ERPNext.Client   ( getDocTypeList   , getDocType@@ -29,11 +40,16 @@ import ERPNext.Client.Helper (urlEncode)  -- | Type class for types which represent an ERPNext DocType.--- Each DocType has a unique name.+-- Each DocType has a unique name but there can still be multiple+-- „views“ (i.e. records types) for one DocType. class IsDocType a where   docTypeName :: Text-  -- TODO: implement auto-derive (using typename and generic)? +{-|+  Get a list of all documents of a given DocType.+  The 'QueryStringParam's can select fields, filter, order, enable+  paging, and more.+-} getDocTypeList :: forall a. (IsDocType a, FromJSON a)                => Manager -> Config  -> [QueryStringParam]-> IO (ApiResponse [a]) getDocTypeList manager config qsParams = do@@ -42,6 +58,7 @@   response <- httpLbs request manager   return $ parseGetResponse response +-- | Get a single document of a given DocType by name. getDocType :: forall a. (IsDocType a, FromJSON a)            => Manager -> Config -> Text -> IO (ApiResponse a) getDocType manager config name = do@@ -50,13 +67,13 @@   response <- httpLbs request manager   return $ parseGetResponse response -{- | Delete a named object.+{- | Delete a single document of a given DocType by name.  The phantom type parameter @a@ is used to figure out the DocType. A customer can be deleted like this:  @-res <- deleteDocType @Customer manager config "<customer name>"+res \<- deleteDocType @Customer manager config "customer name" @ -} deleteDocType :: forall a. (IsDocType a)@@ -67,6 +84,7 @@   response <- httpLbs request manager   return $ parseDeleteResponse response +-- | Create a new document of a given DocType. postDocType :: forall a. (IsDocType a, FromJSON a, ToJSON a)             => Manager -> Config -> a -> IO (ApiResponse a) postDocType manager config doc = do@@ -75,6 +93,7 @@   response <- httpLbs request manager   return $ parseGetResponse response +-- | Update a document of a given DocType by name. putDocType :: forall a. (IsDocType a, FromJSON a, ToJSON a)            => Manager -> Config -> Text -> a -> IO (ApiResponse a) putDocType manager config name doc = do@@ -83,7 +102,7 @@   response <- httpLbs request manager   return $ parseGetResponse response -+-- | Create an API client configuration. mkConfig :: Text -> Text -> Secret -> Config mkConfig baseUrl apiKey apiSecret = Config   { baseUrl = baseUrl@@ -96,7 +115,7 @@ mkSecret = Secret  --- | Create the API Request.+-- | Create the API 'Request'. createRequest :: Config -> Text -> BS.ByteString -> IO Request createRequest config path method = do   request <- parseRequest $ unpack (baseUrl config <> path)@@ -105,6 +124,7 @@     , requestHeaders = [mkAuthHeader config]     } +-- | Create the API 'Request' with a JSON body. createRequestWithBody :: ToJSON a => Config -> Text -> BS.ByteString -> a -> IO Request createRequestWithBody config path method doc = do   request <- parseRequest $ unpack (baseUrl config <> path)@@ -126,6 +146,7 @@   { getSecret :: Text   } +-- | Data wrapper type just to parse the JSON returned by ERPNext. data DataWrapper a = DataWrapper { getData :: a }   deriving Show @@ -134,11 +155,20 @@     dataValue <- obj .: "data"     return (DataWrapper dataValue) +-- | The API response. data ApiResponse a-  = Ok (Response LBS.ByteString) Value a-  | Err (Response LBS.ByteString) (Maybe (Value, Text))+  = Ok -- ^ The OK response.+      (Response LBS.ByteString) -- ^ The server's full response including header information.+      Value -- ^ The returned JSON.+      a -- ^ The result parsed from the returned JSON.+  | Err -- ^ The error response.+      (Response LBS.ByteString) -- ^ The server's full response including header information.+      (Maybe (Value, Text)) -- ^ If the response is valid JSON, 'Just' the returned JSON and+                            -- the parse error message telling why 'Value' couldn't be parsed+                            -- into @a@.   deriving Show +-- | Get the full response from the API response. getResponse :: ApiResponse a -> Response LBS.ByteString getResponse (Ok r _ _) = r getResponse (Err r _) = r
src/ERPNext/Client/Filters.hs view
@@ -81,6 +81,7 @@     <> renderFilterValue (filterValue f)     <> "]" +-- | Render the filter terms for the URL query string. renderFilters :: Text -> [Filter] -> Text renderFilters prefix filters =   let
src/ERPNext/Client/Helper.hs view
@@ -10,15 +10,19 @@ import Data.Text qualified as T import Network.URI (escapeURIString, isUnreserved) +-- | Percent-encode string for use in a URL. urlEncode :: Text -> Text urlEncode = pack . escapeURIString isUnreserved . unpack  sanitizeQuotes :: Text -> Text sanitizeQuotes = T.filter (/= '"') +-- TODO: is it a good idea to just drop "?+-- | Double-quote string after dropping existing double quotes. quote :: Text -> Text quote t = "\"" <> sanitizeQuotes t <> "\"" +-- | 'show' but return 'Text'. tshow :: Show a => a -> Text tshow = pack . show 
src/ERPNext/Client/QueryStringParams.hs view
@@ -14,22 +14,22 @@ -- TODO: Maybe change type or make opaque type to prevent invalid combinations? -- TODO: add variants for limit, offset, etc.? data QueryStringParam-  = Debug Bool -- ^ if 'True', makes API returning query analysis info instead of data-  | AsDict Bool -- ^ if 'False', makes API returning the data records as mixed-type arrays which cannot be parsed by this library (default: 'True')-  | LimitStart Int -- ^ offset-  | LimitPageLength Int -- ^ page size-  | Asc Text-  | Desc Text-  | Fields [Text]-  | AndFilter [Filter]-  | OrFilter [Filter]+  = Debug Bool -- ^ If 'True', makes API returning query analysis info instead of data+  | AsDict Bool -- ^ If 'False', makes API returning the data records as mixed-type arrays which cannot be parsed by this library (default: 'True')+  | LimitStart Int -- ^ Page offset (starts at 0)+  | LimitPageLength Int -- ^ Page size+  | Asc Text -- ^ Ascending order by given field+  | Desc Text -- ^ Descending order by given field+  | Fields [Text] -- ^ Select fields+  | AndFilter [Filter] -- ^ Filter terms combined with logical AND+  | OrFilter [Filter] -- ^ Filter terms combined with logical OR  renderQueryStringParam :: QueryStringParam -> Text renderQueryStringParam qsParam =   case qsParam of     Debug b -> "debug=" <> tshow b     AsDict b -> "as_dict=" <> tshow b-    LimitStart offset -> "limit_start=" <> tshow offset+    LimitStart offset -> "limit_start=" <> tshow (max 0 offset)     LimitPageLength n -> "limit=" <> tshow n -- it was limit_page_length up to v13      Asc field ->@@ -55,5 +55,6 @@ renderOrderBy field order =   "order_by=" <> urlEncode (field <> " " <> order) +-- | Render the query string for the URL. renderQueryStringParams :: [QueryStringParam] -> Text renderQueryStringParams params = intercalate "&" (map renderQueryStringParam params)