packages feed

json-api 0.1.0.4 → 0.1.1.0

raw patch · 12 files changed

+310/−145 lines, 12 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Network.JSONApi.Document: Document :: ResourceData a b -> Maybe Links -> Maybe (Meta c) -> Document a b c
- Network.JSONApi.Document: Error :: Maybe Text -> Maybe Links -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe (Meta a) -> Error a
- Network.JSONApi.Document: Identifier :: Text -> Text -> Identifier
- Network.JSONApi.Document: Meta :: (Map Text a) -> Meta a
- Network.JSONApi.Document: Resource :: Identifier -> a -> Maybe Links -> Maybe (Meta b) -> Maybe (Map Text Relationship) -> Resource a b
- Network.JSONApi.Document: [_data] :: Document a b c -> ResourceData a b
- Network.JSONApi.Document: [_id] :: Identifier -> Text
- Network.JSONApi.Document: [_links] :: Document a b c -> Maybe Links
- Network.JSONApi.Document: [_meta] :: Document a b c -> Maybe (Meta c)
- Network.JSONApi.Document: [_type] :: Identifier -> Text
- Network.JSONApi.Document: [code] :: Error a -> Maybe Text
- Network.JSONApi.Document: [detail] :: Error a -> Maybe Text
- Network.JSONApi.Document: [getIdentifier] :: Resource a b -> Identifier
- Network.JSONApi.Document: [getLinks] :: Resource a b -> Maybe Links
- Network.JSONApi.Document: [getMetaData] :: Resource a b -> Maybe (Meta b)
- Network.JSONApi.Document: [getRelationships] :: Resource a b -> Maybe (Map Text Relationship)
- Network.JSONApi.Document: [getResource] :: Resource a b -> a
- Network.JSONApi.Document: [id] :: Error a -> Maybe Text
- Network.JSONApi.Document: [links] :: Error a -> Maybe Links
- Network.JSONApi.Document: [meta] :: Error a -> Maybe (Meta a)
- Network.JSONApi.Document: [status] :: Error a -> Maybe Text
- Network.JSONApi.Document: [title] :: Error a -> Maybe Text
- Network.JSONApi.Document: data Error a
- Network.JSONApi.Document: data Identifier
- Network.JSONApi.Document: data Links
- Network.JSONApi.Document: data Meta a
- Network.JSONApi.Document: data Relationship
- Network.JSONApi.Document: data Resource a b
- Network.JSONApi.Document: instance (Data.Aeson.Types.Class.FromJSON a, Data.Aeson.Types.Class.FromJSON b) => Data.Aeson.Types.Class.FromJSON (Network.JSONApi.Document.ErrorDocument a b)
- Network.JSONApi.Document: instance (Data.Aeson.Types.Class.FromJSON a, Data.Aeson.Types.Class.FromJSON b) => Data.Aeson.Types.Class.FromJSON (Network.JSONApi.Document.ResourceData a b)
- Network.JSONApi.Document: instance (Data.Aeson.Types.Class.FromJSON a, Data.Aeson.Types.Class.FromJSON b, Data.Aeson.Types.Class.FromJSON c) => Data.Aeson.Types.Class.FromJSON (Network.JSONApi.Document.Document a b c)
- Network.JSONApi.Document: instance (Data.Aeson.Types.Class.ToJSON a, Data.Aeson.Types.Class.ToJSON b) => Data.Aeson.Types.Class.ToJSON (Network.JSONApi.Document.ErrorDocument a b)
- Network.JSONApi.Document: instance (Data.Aeson.Types.Class.ToJSON a, Data.Aeson.Types.Class.ToJSON b) => Data.Aeson.Types.Class.ToJSON (Network.JSONApi.Document.ResourceData a b)
- Network.JSONApi.Document: instance (Data.Aeson.Types.Class.ToJSON a, Data.Aeson.Types.Class.ToJSON b, Data.Aeson.Types.Class.ToJSON c) => Data.Aeson.Types.Class.ToJSON (Network.JSONApi.Document.Document a b c)
- Network.JSONApi.Document: instance (GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (Network.JSONApi.Document.ErrorDocument a b)
- Network.JSONApi.Document: instance (GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (Network.JSONApi.Document.ResourceData a b)
- Network.JSONApi.Document: instance (GHC.Classes.Eq a, GHC.Classes.Eq b, GHC.Classes.Eq c) => GHC.Classes.Eq (Network.JSONApi.Document.Document a b c)
- Network.JSONApi.Document: instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Network.JSONApi.Document.ErrorDocument a b)
- Network.JSONApi.Document: instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Network.JSONApi.Document.ResourceData a b)
- Network.JSONApi.Document: instance (GHC.Show.Show a, GHC.Show.Show b, GHC.Show.Show c) => GHC.Show.Show (Network.JSONApi.Document.Document a b c)
- Network.JSONApi.Document: instance GHC.Generics.Generic (Network.JSONApi.Document.Document a b c)
- Network.JSONApi.Document: instance GHC.Generics.Generic (Network.JSONApi.Document.ErrorDocument a b)
- Network.JSONApi.Document: instance GHC.Generics.Generic (Network.JSONApi.Document.ResourceData a b)
- Network.JSONApi.Document: mkRelationship :: Maybe Identifier -> Maybe Links -> Maybe Relationship
- Network.JSONApi.Document: toLinks :: [(Rel, URL)] -> Links
- Network.JSONApi.Error: instance GHC.Classes.Eq a => GHC.Classes.Eq (Network.JSONApi.Error.Error a)
- Network.JSONApi.Error: instance GHC.Show.Show a => GHC.Show.Show (Network.JSONApi.Error.Error a)
- Network.JSONApi.Meta: instance Data.Aeson.Types.Class.FromJSON a => Data.Aeson.Types.Class.FromJSON (Network.JSONApi.Meta.Meta a)
- Network.JSONApi.Meta: instance Data.Aeson.Types.Class.ToJSON a => Data.Aeson.Types.Class.ToJSON (Network.JSONApi.Meta.Meta a)
- Network.JSONApi.Meta: instance GHC.Classes.Eq a => GHC.Classes.Eq (Network.JSONApi.Meta.Meta a)
- Network.JSONApi.Meta: instance GHC.Classes.Ord a => GHC.Classes.Ord (Network.JSONApi.Meta.Meta a)
- Network.JSONApi.Meta: instance GHC.Generics.Generic (Network.JSONApi.Meta.Meta a)
- Network.JSONApi.Meta: instance GHC.Show.Show a => GHC.Show.Show (Network.JSONApi.Meta.Meta a)
- Network.JSONApi.Resource: instance (Data.Aeson.Types.Class.FromJSON a, Data.Aeson.Types.Class.FromJSON b) => Data.Aeson.Types.Class.FromJSON (Network.JSONApi.Resource.Resource a b)
- Network.JSONApi.Resource: instance (Data.Aeson.Types.Class.ToJSON a, Data.Aeson.Types.Class.ToJSON b) => Data.Aeson.Types.Class.ToJSON (Network.JSONApi.Resource.Resource a b)
- Network.JSONApi.Resource: instance (GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (Network.JSONApi.Resource.Resource a b)
- Network.JSONApi.Resource: instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Network.JSONApi.Resource.Resource a b)
- Network.JSONApi.Resource: instance GHC.Generics.Generic (Network.JSONApi.Resource.Resource a b)
+ Network.JSONApi: Error :: Maybe Text -> Maybe Links -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Meta -> Error a
+ Network.JSONApi: ErrorDocument :: Error a -> Maybe Links -> Maybe Meta -> ErrorDocument a
+ Network.JSONApi: Identifier :: Text -> Text -> Identifier
+ Network.JSONApi: List :: [Resource a] -> ResourceData a
+ Network.JSONApi: Meta :: Object -> Meta
+ Network.JSONApi: Resource :: Identifier -> a -> Maybe Links -> Maybe Meta -> Maybe (Map Text Relationship) -> Resource a
+ Network.JSONApi: Singleton :: (Resource a) -> ResourceData a
+ Network.JSONApi: [_errorLinks] :: ErrorDocument a -> Maybe Links
+ Network.JSONApi: [_errorMeta] :: ErrorDocument a -> Maybe Meta
+ Network.JSONApi: [_error] :: ErrorDocument a -> Error a
+ Network.JSONApi: [_id] :: Identifier -> Text
+ Network.JSONApi: [_type] :: Identifier -> Text
+ Network.JSONApi: [code] :: Error a -> Maybe Text
+ Network.JSONApi: [detail] :: Error a -> Maybe Text
+ Network.JSONApi: [getIdentifier] :: Resource a -> Identifier
+ Network.JSONApi: [getLinks] :: Resource a -> Maybe Links
+ Network.JSONApi: [getMetaData] :: Resource a -> Maybe Meta
+ Network.JSONApi: [getRelationships] :: Resource a -> Maybe (Map Text Relationship)
+ Network.JSONApi: [getResource] :: Resource a -> a
+ Network.JSONApi: [id] :: Error a -> Maybe Text
+ Network.JSONApi: [links] :: Error a -> Maybe Links
+ Network.JSONApi: [meta] :: Error a -> Maybe Meta
+ Network.JSONApi: [status] :: Error a -> Maybe Text
+ Network.JSONApi: [title] :: Error a -> Maybe Text
+ Network.JSONApi: class (ToJSON a) => MetaObject a
+ Network.JSONApi: class (ToJSON a, FromJSON a) => ResourcefulEntity a where fromResource = getResource toResource a = Resource (Identifier (resourceIdentifier a) (resourceType a)) a (resourceLinks a) (resourceMetaData a) (resourceRelationships a)
+ Network.JSONApi: data Document a
+ Network.JSONApi: data Error a
+ Network.JSONApi: data ErrorDocument a
+ Network.JSONApi: data Identifier
+ Network.JSONApi: data Links
+ Network.JSONApi: data Meta
+ Network.JSONApi: data Relationship
+ Network.JSONApi: data Resource a
+ Network.JSONApi: data ResourceData a
+ Network.JSONApi: fromResource :: ResourcefulEntity a => Resource a -> a
+ Network.JSONApi: mkDocument :: ResourcefulEntity a => [a] -> Maybe Links -> Maybe Meta -> [Value] -> Document a
+ Network.JSONApi: mkMeta :: (MetaObject a) => a -> Meta
+ Network.JSONApi: mkRelationship :: Maybe Identifier -> Maybe Links -> Maybe Relationship
+ Network.JSONApi: resourceIdentifier :: ResourcefulEntity a => a -> Text
+ Network.JSONApi: resourceLinks :: ResourcefulEntity a => a -> Maybe Links
+ Network.JSONApi: resourceMetaData :: ResourcefulEntity a => a -> Maybe Meta
+ Network.JSONApi: resourceRelationships :: ResourcefulEntity a => a -> Maybe (Map Text Relationship)
+ Network.JSONApi: resourceType :: ResourcefulEntity a => a -> Text
+ Network.JSONApi: toLinks :: [(Rel, URL)] -> Links
+ Network.JSONApi: toResource :: ResourcefulEntity a => a -> Resource a
+ Network.JSONApi: typeName :: MetaObject a => a -> Text
+ Network.JSONApi.Document: instance Data.Aeson.Types.Class.FromJSON a => Data.Aeson.Types.Class.FromJSON (Network.JSONApi.Document.Document a)
+ Network.JSONApi.Document: instance Data.Aeson.Types.Class.FromJSON a => Data.Aeson.Types.Class.FromJSON (Network.JSONApi.Document.ErrorDocument a)
+ Network.JSONApi.Document: instance Data.Aeson.Types.Class.FromJSON a => Data.Aeson.Types.Class.FromJSON (Network.JSONApi.Document.ResourceData a)
+ Network.JSONApi.Document: instance Data.Aeson.Types.Class.ToJSON a => Data.Aeson.Types.Class.ToJSON (Network.JSONApi.Document.Document a)
+ Network.JSONApi.Document: instance Data.Aeson.Types.Class.ToJSON a => Data.Aeson.Types.Class.ToJSON (Network.JSONApi.Document.ErrorDocument a)
+ Network.JSONApi.Document: instance Data.Aeson.Types.Class.ToJSON a => Data.Aeson.Types.Class.ToJSON (Network.JSONApi.Document.ResourceData a)
+ Network.JSONApi.Document: instance GHC.Classes.Eq (Network.JSONApi.Document.ErrorDocument a)
+ Network.JSONApi.Document: instance GHC.Classes.Eq a => GHC.Classes.Eq (Network.JSONApi.Document.Document a)
+ Network.JSONApi.Document: instance GHC.Classes.Eq a => GHC.Classes.Eq (Network.JSONApi.Document.ResourceData a)
+ Network.JSONApi.Document: instance GHC.Generics.Generic (Network.JSONApi.Document.Document a)
+ Network.JSONApi.Document: instance GHC.Generics.Generic (Network.JSONApi.Document.ErrorDocument a)
+ Network.JSONApi.Document: instance GHC.Generics.Generic (Network.JSONApi.Document.ResourceData a)
+ Network.JSONApi.Document: instance GHC.Show.Show (Network.JSONApi.Document.ErrorDocument a)
+ Network.JSONApi.Document: instance GHC.Show.Show a => GHC.Show.Show (Network.JSONApi.Document.Document a)
+ Network.JSONApi.Document: instance GHC.Show.Show a => GHC.Show.Show (Network.JSONApi.Document.ResourceData a)
+ Network.JSONApi.Document: mkDocument :: ResourcefulEntity a => [a] -> Maybe Links -> Maybe Meta -> [Value] -> Document a
+ Network.JSONApi.Error: instance GHC.Classes.Eq (Network.JSONApi.Error.Error a)
+ Network.JSONApi.Error: instance GHC.Show.Show (Network.JSONApi.Error.Error a)
+ Network.JSONApi.Meta: class (ToJSON a) => MetaObject a
+ Network.JSONApi.Meta: instance Data.Aeson.Types.Class.FromJSON Network.JSONApi.Meta.Meta
+ Network.JSONApi.Meta: instance Data.Aeson.Types.Class.ToJSON Network.JSONApi.Meta.Meta
+ Network.JSONApi.Meta: instance GHC.Base.Monoid Network.JSONApi.Meta.Meta
+ Network.JSONApi.Meta: instance GHC.Classes.Eq Network.JSONApi.Meta.Meta
+ Network.JSONApi.Meta: instance GHC.Generics.Generic Network.JSONApi.Meta.Meta
+ Network.JSONApi.Meta: instance GHC.Show.Show Network.JSONApi.Meta.Meta
+ Network.JSONApi.Meta: mkMeta :: (MetaObject a) => a -> Meta
+ Network.JSONApi.Meta: typeName :: MetaObject a => a -> Text
+ Network.JSONApi.Resource: class (ToJSON a, FromJSON a) => ResourcefulEntity a where fromResource = getResource toResource a = Resource (Identifier (resourceIdentifier a) (resourceType a)) a (resourceLinks a) (resourceMetaData a) (resourceRelationships a)
+ Network.JSONApi.Resource: fromResource :: ResourcefulEntity a => Resource a -> a
+ Network.JSONApi.Resource: instance Data.Aeson.Types.Class.FromJSON a => Data.Aeson.Types.Class.FromJSON (Network.JSONApi.Resource.Resource a)
+ Network.JSONApi.Resource: instance Data.Aeson.Types.Class.ToJSON a => Data.Aeson.Types.Class.ToJSON (Network.JSONApi.Resource.Resource a)
+ Network.JSONApi.Resource: instance GHC.Classes.Eq a => GHC.Classes.Eq (Network.JSONApi.Resource.Resource a)
+ Network.JSONApi.Resource: instance GHC.Generics.Generic (Network.JSONApi.Resource.Resource a)
+ Network.JSONApi.Resource: instance GHC.Show.Show a => GHC.Show.Show (Network.JSONApi.Resource.Resource a)
+ Network.JSONApi.Resource: resourceIdentifier :: ResourcefulEntity a => a -> Text
+ Network.JSONApi.Resource: resourceLinks :: ResourcefulEntity a => a -> Maybe Links
+ Network.JSONApi.Resource: resourceMetaData :: ResourcefulEntity a => a -> Maybe Meta
+ Network.JSONApi.Resource: resourceRelationships :: ResourcefulEntity a => a -> Maybe (Map Text Relationship)
+ Network.JSONApi.Resource: resourceType :: ResourcefulEntity a => a -> Text
+ Network.JSONApi.Resource: toResource :: ResourcefulEntity a => a -> Resource a
- Network.JSONApi.Document: ErrorDocument :: Error a -> Maybe Links -> Maybe (Meta b) -> ErrorDocument a b
+ Network.JSONApi.Document: ErrorDocument :: Error a -> Maybe Links -> Maybe Meta -> ErrorDocument a
- Network.JSONApi.Document: List :: [Resource a b] -> ResourceData a b
+ Network.JSONApi.Document: List :: [Resource a] -> ResourceData a
- Network.JSONApi.Document: Singleton :: (Resource a b) -> ResourceData a b
+ Network.JSONApi.Document: Singleton :: (Resource a) -> ResourceData a
- Network.JSONApi.Document: [_errorLinks] :: ErrorDocument a b -> Maybe Links
+ Network.JSONApi.Document: [_errorLinks] :: ErrorDocument a -> Maybe Links
- Network.JSONApi.Document: [_errorMeta] :: ErrorDocument a b -> Maybe (Meta b)
+ Network.JSONApi.Document: [_errorMeta] :: ErrorDocument a -> Maybe Meta
- Network.JSONApi.Document: [_error] :: ErrorDocument a b -> Error a
+ Network.JSONApi.Document: [_error] :: ErrorDocument a -> Error a
- Network.JSONApi.Document: data Document a b c
+ Network.JSONApi.Document: data Document a
- Network.JSONApi.Document: data ErrorDocument a b
+ Network.JSONApi.Document: data ErrorDocument a
- Network.JSONApi.Document: data ResourceData a b
+ Network.JSONApi.Document: data ResourceData a
- Network.JSONApi.Error: Error :: Maybe Text -> Maybe Links -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe (Meta a) -> Error a
+ Network.JSONApi.Error: Error :: Maybe Text -> Maybe Links -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Text -> Maybe Meta -> Error a
- Network.JSONApi.Error: [meta] :: Error a -> Maybe (Meta a)
+ Network.JSONApi.Error: [meta] :: Error a -> Maybe Meta
- Network.JSONApi.Meta: Meta :: (Map Text a) -> Meta a
+ Network.JSONApi.Meta: Meta :: Object -> Meta
- Network.JSONApi.Meta: data Meta a
+ Network.JSONApi.Meta: data Meta
- Network.JSONApi.Resource: Resource :: Identifier -> a -> Maybe Links -> Maybe (Meta b) -> Maybe (Map Text Relationship) -> Resource a b
+ Network.JSONApi.Resource: Resource :: Identifier -> a -> Maybe Links -> Maybe Meta -> Maybe (Map Text Relationship) -> Resource a
- Network.JSONApi.Resource: [getIdentifier] :: Resource a b -> Identifier
+ Network.JSONApi.Resource: [getIdentifier] :: Resource a -> Identifier
- Network.JSONApi.Resource: [getLinks] :: Resource a b -> Maybe Links
+ Network.JSONApi.Resource: [getLinks] :: Resource a -> Maybe Links
- Network.JSONApi.Resource: [getMetaData] :: Resource a b -> Maybe (Meta b)
+ Network.JSONApi.Resource: [getMetaData] :: Resource a -> Maybe Meta
- Network.JSONApi.Resource: [getRelationships] :: Resource a b -> Maybe (Map Text Relationship)
+ Network.JSONApi.Resource: [getRelationships] :: Resource a -> Maybe (Map Text Relationship)
- Network.JSONApi.Resource: [getResource] :: Resource a b -> a
+ Network.JSONApi.Resource: [getResource] :: Resource a -> a
- Network.JSONApi.Resource: data Resource a b
+ Network.JSONApi.Resource: data Resource a

Files

example/example.cabal view
@@ -35,6 +35,7 @@                , servant-server >= 0.7.1 && < 0.8.0                , text           >= 1.2.2.1 && < 1.2.3.0                , url            >= 2.1.3 && < 2.2.0+               , unordered-containers >= 0.2.7.1 && < 0.2.8.0                , wai            >= 3.2.1.1 && < 3.2.2.0                , warp 
json-api.cabal view
@@ -1,5 +1,5 @@ name:                json-api-version:             0.1.0.4+version:             0.1.1.0 homepage:            https://github.com/toddmohney/json-api bug-reports:         https://github.com/toddmohney/json-api/issues license:             MIT@@ -52,6 +52,7 @@     RecordWildCards    exposed-modules:+    Network.JSONApi     Network.JSONApi.Error     Network.JSONApi.Document     Network.JSONApi.Meta@@ -80,6 +81,7 @@                , lens         >= 4.13 && < 5.0                , lens-aeson   >= 1.0.0.5 && < 1.0.1.0                , text         >= 1.2.2.1 && < 1.2.3.0+               , unordered-containers >= 0.2.7.1 && < 0.2.8.0                , url          >= 2.1.3 && < 2.2.0    default-language:    Haskell2010@@ -94,7 +96,6 @@     -Wall     -fwarn-unused-matches     -fwarn-unused-binds-    -fwarn-unused-imports    hs-source-dirs: test 
+ src/Network/JSONApi.hs view
@@ -0,0 +1,28 @@+{- |+Entry-point module for this package.++Contains representations of the top-level JSON-API document structure.+-}+module Network.JSONApi+  ( D.Document+  , D.ErrorDocument (..)+  , E.Error (..)+  , D.ResourceData (..)+  , R.Relationship+  , R.Resource (..)+  , R.ResourcefulEntity (..)+  , R.Identifier (..)+  , L.Links+  , M.Meta (..)+  , M.MetaObject (..)+  , L.toLinks+  , R.mkRelationship+  , D.mkDocument+  , M.mkMeta+  ) where++import qualified Network.JSONApi.Error as E+import qualified Network.JSONApi.Document as D+import qualified Network.JSONApi.Link as L+import qualified Network.JSONApi.Meta as M+import qualified Network.JSONApi.Resource as R
src/Network/JSONApi/Document.hs view
@@ -4,23 +4,17 @@ Contains representations of the top-level JSON-API document structure. -} module Network.JSONApi.Document-( Document (..)+( Document , ErrorDocument (..)-, E.Error (..) , ResourceData (..)-, R.Relationship-, R.Resource (..)-, R.Identifier (..)-, L.Links-, M.Meta (..)-, L.toLinks-, R.mkRelationship+, mkDocument ) where  import Control.Monad (mzero) import Data.Aeson   ( ToJSON   , FromJSON+  , Value   , (.=)   , (.:)   , (.:?)@@ -30,7 +24,7 @@ import qualified Network.JSONApi.Error as E import Network.JSONApi.Link as L import Network.JSONApi.Meta as M-import Network.JSONApi.Resource (Resource)+import Network.JSONApi.Resource (Resource, ResourcefulEntity) import qualified Network.JSONApi.Resource as R  {- |@@ -41,13 +35,58 @@  For more information see: <http://jsonapi.org/format/#document-top-level> -}-data Document a b c = Document-  { _data  ::  ResourceData a b+data Document a = Document+  { _data  ::  ResourceData a   , _links ::  Maybe Links-  , _meta  ::  Maybe (Meta c)+  , _meta  ::  Maybe Meta+  , _included :: [Value]   } deriving (Show, Eq, G.Generic) +instance (ToJSON a)+      => ToJSON (Document a) where+  toJSON (Document (List res) links meta included) =+    AE.object [ "data"  .= res+              , "links" .= links+              , "meta"  .= meta+              , "included" .= included+              ]+  toJSON (Document (Singleton res) links meta included) =+    AE.object [ "data"  .= res+              , "links" .= links+              , "meta"  .= meta+              , "included" .= included+              ]++instance (FromJSON a) => FromJSON (Document a) where+  parseJSON = AE.withObject "document" $ \v -> do+    d <- v .:  "data"+    l <- v .:? "links"+    m <- v .:? "meta"+    i <- v .: "included"+    return (Document d l m i)+ {- |+Constructor function for the Document data type.+-}+mkDocument :: ResourcefulEntity a =>+              [a]+           -> Maybe Links+           -> Maybe Meta+           -> [Value]+           -> Document a+mkDocument res links meta included =+  Document+    { _data = toResourceData res+    , _links = links+    , _meta = meta+    , _included = included+    }++toResourceData :: ResourcefulEntity a => [a] -> ResourceData a+toResourceData (r:[]) = Singleton (R.toResource r)+toResourceData rs      = List (map R.toResource rs)++{- | The @Resource@ type encapsulates the underlying 'Resource'  Included in the top-level 'Document', the @Resource@ may be either@@ -55,38 +94,19 @@  For more information see: <http://jsonapi.org/format/#document-top-level> -}-data ResourceData a b = Singleton (Resource a b)-                      | List [Resource a b]+data ResourceData a = Singleton (Resource a)+                      | List [ Resource a ]                       deriving (Show, Eq, G.Generic) -instance (ToJSON a, ToJSON b) => ToJSON (ResourceData a b) where+instance (ToJSON a) => ToJSON (ResourceData a) where   toJSON (Singleton res) = AE.toJSON res   toJSON (List res)      = AE.toJSON res -instance (FromJSON a, FromJSON b) => FromJSON (ResourceData a b) where+instance (FromJSON a) => FromJSON (ResourceData a) where   parseJSON (AE.Object v) = Singleton <$> (AE.parseJSON (AE.Object v))   parseJSON (AE.Array v)  = List <$> (AE.parseJSON (AE.Array v))   parseJSON _             = mzero -instance (ToJSON a, ToJSON b, ToJSON c) => ToJSON (Document a b c) where-  toJSON (Document (List res) links meta) =-    AE.object [ "data"  .= res-              , "links" .= links-              , "meta"  .= meta-              ]-  toJSON (Document (Singleton res) links meta) =-    AE.object [ "data"  .= res-              , "links" .= links-              , "meta"  .= meta-              ]--instance (FromJSON a, FromJSON b, FromJSON c) => FromJSON (Document a b c) where-  parseJSON = AE.withObject "document" $ \v -> do-    d <- v .:  "data"-    l <- v .:? "links"-    m <- v .:? "meta"-    return (Document d l m)- {- | The @ErrorDocument@ type represents the alternative form of the top-level JSON-API requirement.@@ -96,20 +116,20 @@  For more information see: <http://jsonapi.org/format/#errors> -}-data ErrorDocument a b = ErrorDocument+data ErrorDocument a = ErrorDocument   { _error :: E.Error a   , _errorLinks :: Maybe Links-  , _errorMeta  :: Maybe (Meta b)+  , _errorMeta  :: Maybe Meta   } deriving (Show, Eq, G.Generic) -instance (ToJSON a, ToJSON b) => ToJSON (ErrorDocument a b) where+instance (ToJSON a) => ToJSON (ErrorDocument a) where   toJSON (ErrorDocument err links meta) =     AE.object [ "error" .= err               , "links" .= links               , "meta"  .= meta               ] -instance (FromJSON a, FromJSON b) => FromJSON (ErrorDocument a b) where+instance (FromJSON a) => FromJSON (ErrorDocument a) where   parseJSON = AE.withObject "error" $ \v ->     ErrorDocument       <$> v .: "error"
src/Network/JSONApi/Error.hs view
@@ -31,7 +31,7 @@         , code   :: Maybe Text         , title  :: Maybe Text         , detail :: Maybe Text-        , meta   :: Maybe (Meta a)+        , meta   :: Maybe Meta         }   deriving (Show, Eq, G.Generic) 
src/Network/JSONApi/Meta.hs view
@@ -3,10 +3,14 @@  Specification: <http://jsonapi.org/format/#document-meta> -}-module Network.JSONApi.Meta where+module Network.JSONApi.Meta+( Meta (..)+, MetaObject (..)+, mkMeta+)where -import Data.Aeson (ToJSON, FromJSON)-import Data.Map (Map)+import Data.Aeson (ToJSON, FromJSON, Object, toJSON)+import Data.HashMap.Strict as HM import Data.Text (Text) import qualified GHC.Generics as G @@ -31,8 +35,41 @@  Specification: <http://jsonapi.org/format/#document-meta> -}-data Meta a = Meta (Map Text a)-  deriving (Show, Eq, Ord, G.Generic)+data Meta = Meta Object+  deriving (Show, Eq, G.Generic) -instance ToJSON a   => ToJSON (Meta a)-instance FromJSON a => FromJSON (Meta a)+instance ToJSON Meta+instance FromJSON Meta++instance Monoid Meta where+  mappend (Meta a) (Meta b) = Meta $ HM.union a b+  mempty = Meta $ HM.empty++{- |+Convienience class for constructing a Meta type++Example usage:+@+data Pagination = Pagination+  { currentPage :: Int+  , totalPages :: Int+  } deriving (Show, Generic)++instance ToJSON Pagination+instance MetaObject Pagination where+  typeName _ = "pagination"+@+-}+class (ToJSON a) => MetaObject a where+  typeName :: a -> Text++{- |+Convienience constructor function for the Meta type++Useful on its own or in combination with Meta's monoid instance++Example usage:+See MetaSpec.hs for an example+-}+mkMeta :: (MetaObject a) => a -> Meta+mkMeta obj = Meta $ HM.singleton (typeName obj) (toJSON obj)
src/Network/JSONApi/Resource.hs view
@@ -6,6 +6,7 @@ module Network.JSONApi.Resource ( Identifier (..) , Resource (..)+, ResourcefulEntity (..) , Relationship , mkRelationship ) where@@ -28,15 +29,15 @@  Specification: <http://jsonapi.org/format/#document-resource-objects> -}-data Resource a b = Resource+data Resource a = Resource   { getIdentifier :: Identifier   , getResource :: a   , getLinks :: Maybe Links-  , getMetaData :: Maybe (Meta b)+  , getMetaData :: Maybe Meta   , getRelationships :: Maybe (Map Text Relationship)   } deriving (Show, Eq, G.Generic) -instance (ToJSON a, ToJSON b) => ToJSON (Resource a b) where+instance (ToJSON a) => ToJSON (Resource a) where   toJSON (Resource (Identifier resId resType) resObj linksObj metaObj rels) =     AE.object [ "id"            .= resId               , "type"          .= resType@@ -46,7 +47,7 @@               , "relationships" .= rels               ] -instance (FromJSON a, FromJSON b) => FromJSON (Resource a b) where+instance (FromJSON a) => FromJSON (Resource a) where   parseJSON = AE.withObject "resourceObject" $ \v -> do     id    <- v .: "id"     typ   <- v .: "type"@@ -56,8 +57,27 @@     rels  <- v .:? "relationships"     return $ Resource (Identifier id typ) attrs links meta rels +{- |+A typeclass for decorating an entity with JSON API properties+-}+class (ToJSON a, FromJSON a) => ResourcefulEntity a where+  resourceIdentifier :: a -> Text+  resourceType :: a -> Text+  resourceLinks :: a -> Maybe Links+  resourceMetaData :: a -> Maybe Meta+  resourceRelationships :: a -> Maybe (Map Text Relationship) +  fromResource :: Resource a -> a+  fromResource = getResource +  toResource :: a -> Resource a+  toResource a =+    Resource+      (Identifier (resourceIdentifier a) (resourceType a))+      a+      (resourceLinks a)+      (resourceMetaData a)+      (resourceRelationships a)  {- | A type representing the Relationship between 2 entities@@ -88,8 +108,6 @@ mkRelationship :: Maybe Identifier -> Maybe Links -> Maybe Relationship mkRelationship Nothing Nothing = Nothing mkRelationship resId links = Just $ Relationship resId links--  {- | Identifiers are used to encapsulate the minimum amount of information
test/Network/JSONApi/DocumentSpec.hs view
@@ -1,16 +1,16 @@ module Network.JSONApi.DocumentSpec where -import           Control.Lens ((^?))-import           Data.Aeson (ToJSON)+import Control.Lens ((^?))+import Data.Aeson (ToJSON) import qualified Data.Aeson as AE import qualified Data.Aeson.Lens as Lens-import           Data.ByteString.Lazy.Char8 (ByteString)+import Data.ByteString.Lazy.Char8 (ByteString) {- import qualified Data.ByteString.Lazy.Char8 as BS -}-import           Data.Either (isRight)-import           Data.Maybe-import           Network.JSONApi.Document-import           TestHelpers-import           Test.Hspec+import Data.Either (isRight)+import Data.Maybe+import Network.JSONApi.Document+import TestHelpers+import Test.Hspec  main :: IO () main = hspec spec@@ -19,8 +19,8 @@ spec =   describe "JSON serialization" $ do     it "JSON encodes/decodes a singleton resource" $ do-      let resource = toResource testObject-      let jsonApiObj = Document (Singleton resource) emptyLinks emptyMeta+      -- TODO: test the main resource actually is a singleton+      let jsonApiObj = mkDocument [testObject] Nothing Nothing []       let encodedJson = encodeDocumentObject jsonApiObj       let decodedJson = decodeDocumentObject encodedJson       {- putStrLn (BS.unpack encodedJson) -}@@ -28,8 +28,8 @@       isRight decodedJson `shouldBe` True      it "JSON encodes/decodes a list of resources" $ do-      let resources = [toResource testObject, toResource testObject2]-      let jsonApiObj = Document (List resources) emptyLinks emptyMeta+      -- TODO: test the main resource actually is a list+      let jsonApiObj = mkDocument [testObject, testObject2] Nothing Nothing []       let encodedJson = encodeDocumentObject jsonApiObj       let decodedJson = decodeDocumentObject encodedJson       {- putStrLn (BS.unpack encodedJson) -}@@ -37,47 +37,44 @@       isRight decodedJson `shouldBe` True      it "contains the allowable top-level keys" $ do-      let resource = toResource testObject-      let jsonApiObj = Document (Singleton resource) emptyLinks emptyMeta+      let jsonApiObj = mkDocument [testObject] Nothing Nothing []       let encodedJson = encodeDocumentObject jsonApiObj       let dataObject = encodedJson ^? Lens.key "data"       let linksObject = encodedJson ^? Lens.key "links"       let metaObject = encodedJson ^? Lens.key "meta"+      let includedObject = encodedJson ^? Lens.key "included"       isJust dataObject `shouldBe` True       isJust linksObject `shouldBe` True       isJust metaObject `shouldBe` True+      isJust includedObject `shouldBe` True      it "allows an optional top-level links object" $ do-      let resource = toResource testObject-      let jsonApiObj = Document (Singleton resource) (Just linksObj) emptyMeta+      let jsonApiObj = mkDocument [testObject] (Just linksObj) Nothing []       let encodedJson = encodeDocumentObject jsonApiObj       let decodedJson = decodeDocumentObject encodedJson       -- putStrLn (BS.unpack encodedJson)-      -- putStrLn $ show . fromJust $ decodedJson+      -- putStrLn $ show decodedJson       isRight decodedJson `shouldBe` True      it "allows an optional top-level meta object" $ do-      let resource = toResource testObject-      let jsonApiObj = Document (Singleton resource) emptyLinks (Just testMetaObj)+      let jsonApiObj = mkDocument [testObject] Nothing (Just testMetaObj) []       let encodedJson = encodeDocumentObject jsonApiObj       let decodedJson = decodeDocumentObject encodedJson       -- putStrLn (BS.unpack encodedJson)-      -- putStrLn $ show . fromJust $ decodedJson+      -- putStrLn $ show decodedJson       isRight decodedJson `shouldBe` True -    it "allows an optional top-level meta object" $ do-      let resource = toResource testObject-      let jsonApiObj = Document (Singleton resource) (Just linksObj) (Just testMetaObj)+    it "allows a heterogeneous list of related resources" $ do+      let jsonApiObj = mkDocument [testObject] Nothing Nothing [AE.toJSON (toResource' testObject Nothing Nothing), AE.toJSON (toResource' otherTestObject Nothing Nothing)]       let encodedJson = encodeDocumentObject jsonApiObj       let decodedJson = decodeDocumentObject encodedJson-      -- putStrLn (BS.unpack encodedJson)-      -- putStrLn $ show . fromJust $ decodedJson+      {- putStrLn (BS.unpack encodedJson) -}+      {- putStrLn $ show decodedJson -}       isRight decodedJson `shouldBe` True  decodeDocumentObject :: ByteString-                    -> Either String (Document TestResource (Maybe Bool) (Maybe TestMetaObject))+                    -> Either String (Document TestResource) decodeDocumentObject = AE.eitherDecode -encodeDocumentObject :: (ToJSON a, ToJSON b, ToJSON c) => Document a b c -> ByteString+encodeDocumentObject :: (ToJSON a) => Document a -> ByteString encodeDocumentObject = prettyEncode-
test/Network/JSONApi/ErrorSpec.hs view
@@ -13,7 +13,7 @@ main = hspec spec  spec :: Spec-spec =+spec = do   describe "Defaults" $ do     it "provides defaults" $       let expectedDefault = Error@@ -27,6 +27,7 @@             }       in (def::Error Int) `shouldBe` expectedDefault +  describe "JSON serialization" $     it "provides ToJSON/FromJSON instances" $ do       let testError = (def::Error Int)       let encJson = BS.unpack . prettyEncode $ testError
test/Network/JSONApi/MetaSpec.hs view
@@ -1,9 +1,13 @@ module Network.JSONApi.MetaSpec where +import           Data.Aeson (ToJSON) import qualified Data.Aeson as AE import qualified Data.ByteString.Lazy.Char8 as BS-import qualified Data.Map as Map+import qualified Data.HashMap.Strict as HM import           Data.Maybe (isJust)+import           Data.Monoid ((<>))+import           Data.Text (Text)+import           GHC.Generics (Generic) import           Network.JSONApi.Meta import           TestHelpers (prettyEncode) import           Test.Hspec@@ -12,22 +16,55 @@ main = hspec spec  spec :: Spec-spec =-  describe "serialization" $-    -- is there a compelling reason to test this?-    ---    it "serializes/deserializes primitive values" $ do-      let intTestData = Meta . Map.fromList $ [ ("numData", 5 :: Int) ]-      let encIntJson = BS.unpack . prettyEncode $ intTestData-      let decIntJson = AE.decode (BS.pack encIntJson) :: Maybe (Meta Int)+spec = do+  describe "JSON serialization" $ do+    it "serializes/deserializes maps simple heterogeneous values" $ do+      let testMeta = Meta . HM.fromList $ [ ("numData", AE.toJSON (5 :: Int))+                                          , ("strData", AE.toJSON ("hello" :: String))+                                          ]+      let encIntJson = BS.unpack . prettyEncode $ testMeta+      let decIntJson = AE.decode (BS.pack encIntJson) :: Maybe Meta       isJust decIntJson `shouldBe` True-      -- putStrLn (keys encIntJson)-      -- putStrLn $ show . fromJust $ decIntJson -      let boolTestData = Meta . Map.fromList $ [ ("boolData", True) ]+    it "serializes/deserializes heterogeneous maps of ToJSON types" $ do+      let boolTestData = Meta . HM.fromList $ [ ("objData", AE.toJSON testObject)+                                              , ("otherObjData", AE.toJSON otherTestObject)+                                              ]       let encBoolJson = BS.unpack . prettyEncode $ boolTestData-      let decBoolJson = AE.decode (BS.pack encBoolJson) :: Maybe (Meta Bool)+      let decBoolJson = AE.decode (BS.pack encBoolJson) :: Maybe Meta       isJust decBoolJson `shouldBe` True-      -- putStrLn (keys encBoolJson)-      -- putStrLn $ show . fromJust $ decBoolJson +  describe "monoid instance" $+    it "combines" $ do+      let monoidialConstruction = mkMeta testObject <> mkMeta otherTestObject+      let manualConstruction = Meta . HM.fromList $ [ ("testObject", AE.toJSON testObject)+                                                    , ("otherTestObject", AE.toJSON otherTestObject)+                                                    ]+      monoidialConstruction `shouldBe` manualConstruction++testObject :: TestObject+testObject = TestObject 99 102 "Zapp Brannigan"++otherTestObject :: OtherTestObject+otherTestObject = OtherTestObject "Olive Garden" "Woofers" 29 "TGIFriday's"++data TestObject = TestObject+  { myID :: Int+  , myAge :: Int+  , myName :: Text+  } deriving (Show, Generic)++instance ToJSON TestObject+instance MetaObject TestObject where+  typeName _ = "testObject"++data OtherTestObject = OtherTestObject+  { myFavoriteRestaurant :: Text+  , myDogsName :: Text+  , myDogsAge :: Int+  , myDogsFavoriteRestarant :: Text+  } deriving (Show, Generic)++instance ToJSON OtherTestObject+instance MetaObject OtherTestObject where+  typeName _ = "otherTestObject"
test/Network/JSONApi/ResourceSpec.hs view
@@ -4,6 +4,7 @@ import qualified Data.ByteString.Lazy.Char8 as BS import Data.Map (Map) import qualified Data.Map as Map+import qualified Data.HashMap.Strict as HM import Data.Maybe (isJust, fromJust) import Data.Text (Text, pack) import qualified GHC.Generics as G@@ -19,10 +20,10 @@  spec :: Spec spec =-  describe "ToResource" $+  describe "JSON serialization" $     it "can be encoded and decoded from JSON" $ do       let encodedJson = BS.unpack . prettyEncode $ toResource testObject-      let decodedJson = AE.decode (BS.pack encodedJson) :: Maybe (Resource TestObject (Maybe Int))+      let decodedJson = AE.decode (BS.pack encodedJson) :: Maybe (Resource TestObject)       isJust decodedJson `shouldBe` True       {- putStrLn encodedJson -}       {- putStrLn $ show . fromJust $ decodedJson -}@@ -36,33 +37,30 @@  instance AE.ToJSON TestObject instance AE.FromJSON TestObject--toResource :: TestObject -> Resource TestObject Int-toResource obj =-  Resource-    (Identifier (pack . show . myId $ obj) "TestObject")-    obj-    (Just resourceLinks)-    (Just resourceMetaData)-    (Just resourceRelationships)+instance ResourcefulEntity TestObject where+  resourceIdentifier = pack . show . myId+  resourceType _ = "TestObject"+  resourceLinks _ = Just myResourceLinks+  resourceMetaData _ = Just myResourceMetaData+  resourceRelationships _ = Just myResourceRelationships -resourceRelationships :: Map Text Relationship-resourceRelationships = Map.fromList $ [ ("friends", relationship) ]+myResourceRelationships :: Map Text Relationship+myResourceRelationships = Map.fromList $ [ ("friends", relationship) ]  relationship :: Relationship relationship =   fromJust $ mkRelationship     (Just $ Identifier "42" "FriendOfTestObject")-    (Just resourceLinks)+    (Just myResourceLinks) -resourceLinks :: Links-resourceLinks =+myResourceLinks :: Links+myResourceLinks =   toLinks [ ("self", toURL "/me")           , ("related", toURL "/tacos/4")           ] -resourceMetaData :: Meta Int-resourceMetaData = Meta . Map.fromList $ [ ("extraData", 20) ]+myResourceMetaData :: Meta+myResourceMetaData = Meta . HM.fromList $ [ ("extraData", AE.toJSON (20 :: Int)) ]  toURL :: String -> URL toURL = fromJust . importURL
test/TestHelpers.hs view
@@ -4,11 +4,10 @@ import qualified Data.Aeson.Encode.Pretty as AE import qualified Data.ByteString.Lazy.Char8 as BS import Data.Maybe (fromJust)-import qualified Data.Map as Map-import Data.Monoid ((<>))+import qualified Data.HashMap.Strict as HM import Data.Text (Text, pack)-import qualified GHC.Generics as G-import Network.JSONApi.Document+import GHC.Generics (Generic)+import Network.JSONApi import Network.URL (URL, importURL)  prettyEncode :: AE.ToJSON a => a -> BS.ByteString@@ -17,43 +16,68 @@ prettyConfig :: AE.Config prettyConfig = AE.Config { AE.confIndent = 2, AE.confCompare = mempty } +class HasIdentifiers a where+  uniqueId :: a -> Int+  typeDescriptor :: a -> Text+ data TestResource = TestResource   { myId :: Int   , myName :: Text   , myAge :: Int   , myFavoriteFood :: Text-  } deriving (Show, G.Generic)+  } deriving (Show, Generic) +instance AE.ToJSON TestResource+instance AE.FromJSON TestResource+instance ResourcefulEntity TestResource where+  resourceIdentifier = pack . show . myId+  resourceType _ = "testResource"+  resourceLinks _ = Nothing+  resourceMetaData _ = Nothing+  resourceRelationships _ = Nothing+instance HasIdentifiers TestResource where+  uniqueId = myId+  typeDescriptor _ = "TestResource"++data OtherTestResource = OtherTestResource+  { myFavoriteNumber :: Int+  , myJob :: Text+  , myPay :: Int+  , myEmployer :: Text+  } deriving (Show, Generic)++instance AE.ToJSON OtherTestResource+instance AE.FromJSON OtherTestResource+instance ResourcefulEntity OtherTestResource where+  resourceIdentifier = pack . show . myFavoriteNumber+  resourceType _ = "otherTestResource"+  resourceLinks _ = Nothing+  resourceMetaData _ = Nothing+  resourceRelationships _ = Nothing+instance HasIdentifiers OtherTestResource where+  uniqueId = myFavoriteNumber+  typeDescriptor _ = "OtherTestResource"+ data TestMetaObject = TestMetaObject   { totalPages :: Int   , isSuperFun :: Bool-  } deriving (Show, G.Generic)--instance AE.ToJSON TestResource-instance AE.FromJSON TestResource+  } deriving (Show, Generic)  instance AE.ToJSON TestMetaObject instance AE.FromJSON TestMetaObject -toResource :: TestResource -> Resource TestResource Bool-toResource obj =+toResource' :: (HasIdentifiers a) => a+            -> Maybe Links+            -> Maybe Meta+            -> Resource a+toResource' obj links meta =   Resource-    (Identifier (pack . show . myId $ obj) "TestResource")+    (Identifier (pack . show . uniqueId $ obj) (typeDescriptor obj))     obj-    (Just $ objectLinks obj)-    (Just $ objectMetaData obj)+    links+    meta     Nothing -objectLinks :: TestResource -> Links-objectLinks obj =-  toLinks [ ("self", toURL ("/me/" <> (show $ myId obj)))-          , ("related", toURL ("/friends/" <> (show $ myId obj)))-          ]--objectMetaData :: TestResource -> Meta Bool-objectMetaData obj =-   Meta . Map.fromList $ [ ("isOld", myAge obj > 50) ]- linksObj :: Links linksObj = toLinks [ ("self", toURL "/things/1")                    , ("related", toURL "http://some.domain.com/other/things/1")@@ -65,11 +89,14 @@ testObject2 :: TestResource testObject2 = TestResource 2 "Carrie Brownstein" 35 "Lunch" -testMetaObj :: Meta TestMetaObject+otherTestObject :: OtherTestResource+otherTestObject = OtherTestResource 999 "Atom Smasher" 100 "Atom Smashers, Inc"++testMetaObj :: Meta testMetaObj =-  Meta . Map.fromList $ [ ("importantData", TestMetaObject 3 True) ]+  Meta . HM.fromList $ [ ("importantData", (AE.toJSON $ TestMetaObject 3 True)) ] -emptyMeta :: Maybe (Meta TestMetaObject)+emptyMeta :: Maybe Meta emptyMeta = Nothing  toURL :: String -> URL