Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions persistent-template/Database/Persist/TH.hs
Original file line number Diff line number Diff line change
Expand Up @@ -443,8 +443,8 @@ data MkPersistSettings = MkPersistSettings
--
-- @
-- Just EntityJSON
-- { entityToJSON = 'keyValueEntityToJSON
-- , entityFromJSON = 'keyValueEntityFromJSON
-- { entityToJSON = 'entityIdToJSON
-- , entityFromJSON = 'entityIdFromJSON
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure when this was changed lol

-- }
-- @
, mpsGenerateLenses :: !Bool
Expand Down
74 changes: 73 additions & 1 deletion persistent-test/src/PersistentTest.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE RecordWildCards, UndecidableInstances #-}

module PersistentTest
( module PersistentTest
, cleanDB
Expand Down Expand Up @@ -632,3 +633,74 @@ specsWith runDb = describe "persistent" $ do
fieldComments nameField
`shouldBe`
Just "Fields should be documentable.\n"

describe "JsonEncoding" $ do
let
subject =
JsonEncoding "Bob" 32
subjectEntity =
Entity (JsonEncodingKey (jsonEncodingName subject)) subject

it "encodes without an ID field" $ do
toJSON subjectEntity
`shouldBe`
Object (M.fromList
[ ("name", String "Bob")
, ("age", toJSON (32 :: Int))
, ("id", String "Bob")
])

it "decodes without an ID field" $ do
let
json = encode . Object . M.fromList $
[ ("name", String "Bob")
, ("age", toJSON (32 :: Int))
]
decode json
`shouldBe`
Just subjectEntity

prop "works with a Primary" $ \jsonEncoding -> do
let
ent =
Entity (JsonEncodingKey (jsonEncodingName jsonEncoding)) jsonEncoding
decode (encode ent)
`shouldBe`
Just ent

prop "excuse me what" $ \j@JsonEncoding{..} -> do
let
ent =
Entity (JsonEncodingKey jsonEncodingName) j
toJSON ent
`shouldBe`
Object (M.fromList
[ ("name", toJSON jsonEncodingName)
, ("age", toJSON jsonEncodingAge)
, ("id", toJSON jsonEncodingName)
])

prop "round trip works with composite key" $ \j@JsonEncoding2{..} -> do
let
key = JsonEncoding2Key jsonEncoding2Name jsonEncoding2Blood
ent =
Entity key j
decode (encode ent)
`shouldBe`
Just ent

prop "works with a composite key" $ \j@JsonEncoding2{..} -> do
let
key = JsonEncoding2Key jsonEncoding2Name jsonEncoding2Blood
ent =
Entity key j
toJSON ent
`shouldBe`
Object (M.fromList
[ ("name", toJSON jsonEncoding2Name)
, ("age", toJSON jsonEncoding2Age)
, ("blood", toJSON jsonEncoding2Blood)
, ("id", toJSON key)
])


25 changes: 24 additions & 1 deletion persistent-test/src/PersistentTestModels.hs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE UndecidableInstances #-} -- FIXME
module PersistentTestModels where

import Data.Aeson

import Test.QuickCheck
import Database.Persist.Sql
import Database.Persist.TH
import Init
Expand Down Expand Up @@ -107,7 +109,6 @@ share [mkPersist persistSettings { mpsGeneric = True }, mkMigrate "testMigrate"

MutB
mutA MutAId

|]

deriving instance Show (BackendKey backend) => Show (PetGeneric backend)
Expand All @@ -125,8 +126,30 @@ NoPrefix2
unprefixedLeft Int
unprefixedRight String
deriving Show Eq

|]

share [mkPersist sqlSettings] [persistLowerCase|
JsonEncoding json
name Text
age Int
Primary name
deriving Show Eq

JsonEncoding2 json
name Text
age Int
blood Text
Primary name blood
deriving Show Eq
|]

instance Arbitrary JsonEncoding where
arbitrary = JsonEncoding <$> arbitrary <*> arbitrary

instance Arbitrary JsonEncoding2 where
arbitrary = JsonEncoding2 <$> arbitrary <*> arbitrary <*> arbitrary

deriving instance Show (BackendKey backend) => Show (NoPrefix1Generic backend)
deriving instance Eq (BackendKey backend) => Eq (NoPrefix1Generic backend)

Expand Down
2 changes: 2 additions & 0 deletions persistent/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* [#1063](https://github.com/yesodweb/persistent/pull/1063)
* A new class member `keyFromRecordM` allows you to construct a `Key
record` from a `record` if it was defined with `Primary`.
* [#1036](https://github.com/yesodweb/persistent/pull/1036):
* The method `entityIdFromJSON` that is used to parse entities now correctly works for entities that define a custom `Primary` key.

## 2.10.5.2

Expand Down
16 changes: 11 additions & 5 deletions persistent/Database/Persist/Class/PersistEntity.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module Database.Persist.Class.PersistEntity
, toPersistValueEnum, fromPersistValueEnum
) where

import Data.Aeson (ToJSON (..), FromJSON (..), fromJSON, object, (.:), (.=), Value (Object))
import Data.Aeson (ToJSON (..), withObject, FromJSON (..), fromJSON, object, (.:), (.=), Value (Object))
import qualified Data.Aeson.Parser as AP
import Data.Aeson.Types (Parser,Result(Error,Success))
import Data.Aeson.Text (encodeToTextBuilder)
Expand Down Expand Up @@ -263,8 +263,8 @@ keyValueEntityFromJSON _ = fail "keyValueEntityFromJSON: not an object"
-- @
entityIdToJSON :: (PersistEntity record, ToJSON record) => Entity record -> Value
entityIdToJSON (Entity key value) = case toJSON value of
Object o -> Object $ HM.insert "id" (toJSON key) o
x -> x
Object o -> Object $ HM.insert "id" (toJSON key) o
x -> x

-- | Predefined @parseJSON@. The input JSON looks like
-- @{"id": 1, "name": ...}@.
Expand All @@ -276,8 +276,14 @@ entityIdToJSON (Entity key value) = case toJSON value of
-- parseJSON = entityIdFromJSON
-- @
entityIdFromJSON :: (PersistEntity record, FromJSON record) => Value -> Parser (Entity record)
entityIdFromJSON value@(Object o) = Entity <$> o .: "id" <*> parseJSON value
entityIdFromJSON _ = fail "entityIdFromJSON: not an object"
entityIdFromJSON = withObject "entityIdFromJSON" $ \o -> do
val <- parseJSON (Object o)
k <- case keyFromRecordM of
Nothing ->
o .: "id"
Just func ->
pure $ func val
pure $ Entity k val

instance (PersistEntity record, PersistField record, PersistField (Key record))
=> PersistField (Entity record) where
Expand Down