Repository: stackbuilders/rollbar-haskell
Pull request #81: fix issue: createItem not serializing Nothings correctly
Author: login: jmartinez-sb
Labels: name: dependencies
description: Pull requests that update a dependency file
name: fix
description: This contribution is a fix for a bug
Created: 2025-02-28T11:51:48Z | Merged: 2025-03-14T10:57:52Z

Some fields were reported in the issue as appearing with their values as null, when in fact they shouldn'd have appeared at all in the JSON serialization.

I've included them inside catMaybes so this is corrected. I've also created a couple of tests to determine whether a couple of fields (one of them with Nothing, the other one with a Just value) appear in the JSON serialization. Lastly, I've included a another dependency for the purposes of converting among Text, String, Bytestring and lazy Bytestring mostly for the new tests.


Diff:
diff --git a/rollbar-cli/rollbar-cli.cabal b/rollbar-cli/rollbar-cli.cabal
index a96327f..26935a6 100644
--- a/rollbar-cli/rollbar-cli.cabal
+++ b/rollbar-cli/rollbar-cli.cabal
@@ -68,4 +68,5 @@ test-suite spec
   build-depends:
       base >=4.13 && <5
     , rollbar-cli
+    , bytestring >=0.10 && <1
   default-language: Haskell2010
diff --git a/rollbar-client/rollbar-client.cabal b/rollbar-client/rollbar-client.cabal
index 791927e..62373e9 100644
--- a/rollbar-client/rollbar-client.cabal
+++ b/rollbar-client/rollbar-client.cabal
@@ -99,4 +99,5 @@ test-suite spec
     , text
     , unordered-containers
     , yaml
+    , bytestring >=0.10 && <1
   default-language: Haskell2010
diff --git a/rollbar-client/src/Rollbar/Client/Item.hs b/rollbar-client/src/Rollbar/Client/Item.hs
index 5f78124..120bcc4 100644
--- a/rollbar-client/src/Rollbar/Client/Item.hs
+++ b/rollbar-client/src/Rollbar/Client/Item.hs
@@ -97,15 +97,15 @@ instance ToJSON Item where
     let dataFields =
           [ "environment" .= itemEnvironment
           , "body" .= itemBody
-          , "level" .= itemLevel
-          , "platform" .= itemPlatform
-          , "language" .= itemLanguage
-          , "framework" .= itemFramework
-          , "request" .= itemRequest
-          , "server" .= itemServer
           , "notifier" .= itemNotifier
           ] ++ catMaybes
-          [ ("custom" .=) <$> custom
+          [  ("level" .=) <$> itemLevel
+           , ("platform" .=) <$> itemPlatform
+           , ("language" .=) <$> itemLanguage
+           , ("framework" .=) <$> itemFramework
+           , ("request" .=) <$> itemRequest
+           , ("server" .=) <$> itemServer
+           , ("custom" .=) <$> custom
            , ("fingerprint" .=) <$> fingerprint
            , ("title" .=) <$> title
            , ("uuid" .=) <$> uuid
diff --git a/rollbar-client/test/Rollbar/ClientSpec.hs b/rollbar-client/test/Rollbar/ClientSpec.hs
index c53a4b9..577b5b0 100644
--- a/rollbar-client/test/Rollbar/ClientSpec.hs
+++ b/rollbar-client/test/Rollbar/ClientSpec.hs
@@ -11,7 +11,9 @@ import qualified Data.Aeson.KeyMap as KM
 
 import Control.Monad.Reader
 import Data.Aeson
-import Data.Text
+import qualified Data.ByteString.Lazy as DBL
+import Data.Text.Encoding
+import Data.Text as T
 import Data.Yaml.Config
 import Rollbar.Client
 import Test.Hspec
@@ -143,3 +145,28 @@ spec = do
           reportDeploy deploy
 
         deployId `shouldSatisfy` (> 0)
+
+  describe "ToJSON Item" $ do
+    context "when serializing to JSON" $ do
+      let item = Item
+            { itemEnvironment = Environment "test"
+            , itemBody = Body { bodyPayload = PayloadMessage (Message "Test" mempty) }
+            , itemLevel = Just LevelInfo
+            , itemPlatform = Just "haskell"
+            , itemLanguage = Just "Haskell"
+            , itemFramework = Just "GHC"
+            , itemRequest = Nothing
+            , itemServer = Nothing
+            , custom = Just mempty
+            , title = Nothing
+            , uuid = Just "12345"
+            , fingerprint = Nothing
+            , itemNotifier = Notifier "rollbar-client" "1.1.0"
+            }
+          jsonItem = decodeUtf8 $ DBL.toStrict $ encode item
+
+      it "omits fields if they are Nothing values" $
+        T.unpack jsonItem `shouldNotContain` "\"server\""
+
+      it "includes fields if they are Just values" $ 
+        T.unpack jsonItem `shouldContain` "\"platform\":\"haskell\""
diff --git a/rollbar-wai/package.yaml b/rollbar-wai/package.yaml
index d272c54..efaf38b 100644
--- a/rollbar-wai/package.yaml
+++ b/rollbar-wai/package.yaml
@@ -48,7 +48,7 @@ library:
     - text >= 1.2 && < 2.1
     - unordered-containers >= 0.2 && < 1
     - wai >= 3.2 && < 4
-    - wai-extra >= 3.0 && < 4
+    - wai-extra < 3.1.17         # TODO: update breaks yesod-core, see https://github.com/yesodweb/yesod/issues/1854
 
 executables:
   wai-example:
diff --git a/rollbar-wai/rollbar-wai.cabal b/rollbar-wai/rollbar-wai.cabal
index 2775c08..151534b 100644
--- a/rollbar-wai/rollbar-wai.cabal
+++ b/rollbar-wai/rollbar-wai.cabal
@@ -54,7 +54,7 @@ library
     , text >=1.2 && <2.1
     , unordered-containers >=0.2 && <1
     , wai >=3.2 && <4
-    , wai-extra >=3.0 && <4
+    , wai-extra >=3.0 && < 3.1.17 -- TODO: update breaks yesod-core, see https://github.com/yesodweb/yesod/issues/1854
   default-language: Haskell2010
 
 executable wai-example
@@ -101,4 +101,5 @@ test-suite spec
     , unordered-containers
     , wai
     , warp >=3.3 && <4
+    , bytestring >=0.10 && <1
   default-language: Haskell2010
diff --git a/rollbar-yesod/rollbar-yesod.cabal b/rollbar-yesod/rollbar-yesod.cabal
index b1cf522..692caf5 100644
--- a/rollbar-yesod/rollbar-yesod.cabal
+++ b/rollbar-yesod/rollbar-yesod.cabal
@@ -91,4 +91,5 @@ test-suite spec
     , wai
     , yesod-core
     , yesod-test >=1.6 && <2
+    , bytestring >=0.10 && <1
   default-language: Haskell2010


Reviews:
user: login: DavidMazarro
body: Looks good! Thanks for adding tests! I left a couple of comments not entirely related to your fix or the tests, but that should still be addressed. 
The CI is failing due to an issue with the GitHub Actions runner not being able to use secrets (i.e. the Rollbar token) in external PRs coming from forks. We investigated this a while ago, this is an intentional limitation to avoid security breaches such as people logging or abusing secrets when creating PRs. I've tested your PR locally and can confirm that the tests are passing 🎉
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T10:50:58Z
user: login: jmartinez-sb
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T08:12:00Z
user: login: jmartinez-sb
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T08:16:09Z
user: login: DavidMazarro
body: LGTM! Good job :)
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T10:42:02Z

Review comments:
diff_hunk: @@ -42,6 +42,7 @@ library
       base >=4.13 && <5
     , optparse-applicative >=0.14 && <1
     , rollbar-client >=1.0 && <2
+    , bytestring >=0.10 && <1
path: rollbar-cli/rollbar-cli.cabal
user: login: DavidMazarro
body: I don't think you are making use of the dependency in the library code, so this could be removed.
created_at: 2025-03-11T10:37:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -68,4 +69,5 @@ test-suite spec
   build-depends:
       base >=4.13 && <5
     , rollbar-cli
+    , bytestring >=0.10 && <1
path: rollbar-cli/rollbar-cli.cabal
user: login: DavidMazarro
body: A couple of things on the boundaries:
- Any particular reason for the `0.10` lower bound?
- Having this boundary is good! Consider adding the upper bound in the other test-suites (which only have a lower bound) and both lower and upper bound where bounds are missing entirely.
created_at: 2025-03-11T10:39:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -51,6 +51,7 @@ library
     , unliftio >=0.2 && <1
     , wai >=3.2 && <4
     , yesod-core >=1.6 && <2
+    , bytestring >= 0.10
path: rollbar-yesod/rollbar-yesod.cabal
user: login: DavidMazarro
body: I don't think you are making use of the dependency in the library code, so this could be removed.
created_at: 2025-03-11T10:40:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,7 +54,8 @@ library
     , text >=1.2 && <2.1
     , unordered-containers >=0.2 && <1
     , wai >=3.2 && <4
-    , wai-extra >=3.0 && <4
+    , wai-extra >=3.0 && < 3.1.17  
+    -- TODO: update breaks yesod-core, see https://github.com/yesodweb/yesod/issues/1854
path: rollbar-wai/rollbar-wai.cabal
user: login: DavidMazarro
body: ```suggestion
    , wai-extra >=3.0 && < 3.1.17 -- TODO: upper bound added to avoid breakage, remove when this issue is fixed https://github.com/yesodweb/yesod/issues/1854
```
created_at: 2025-03-11T10:41:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,7 +54,8 @@ library
     , text >=1.2 && <2.1
     , unordered-containers >=0.2 && <1
     , wai >=3.2 && <4
-    , wai-extra >=3.0 && <4
+    , wai-extra >=3.0 && < 3.1.17  
+    -- TODO: update breaks yesod-core, see https://github.com/yesodweb/yesod/issues/1854
path: rollbar-wai/rollbar-wai.cabal
user: login: DavidMazarro
body: I suggested moving the comment to the same line as `wai-extra` (to make sure there is no confusion that it points specifically to that dependency) and added a specific action item for when to remove the TODO.
created_at: 2025-03-11T10:43:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,10 +11,12 @@ import qualified Data.Aeson.KeyMap as KM
 
 import Control.Monad.Reader
 import Data.Aeson
-import Data.Text
+import Data.Text as T
 import Data.Yaml.Config
 import Rollbar.Client
 import Test.Hspec
+import Data.Text.Encoding
+import qualified Data.ByteString.Lazy as DBL
path: rollbar-client/test/Rollbar/ClientSpec.hs
user: login: DavidMazarro
body: Do you mind sorting these imports alphabetically?
created_at: 2025-03-11T10:44:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -143,3 +145,28 @@ spec = do
           reportDeploy deploy
 
         deployId `shouldSatisfy` (> 0)
+
+  describe "ToJSON Item" $ do
+    context "when serializing to JSON" $ do
+      let item = Item
+            { itemEnvironment = Environment "test"
+            , itemBody = Body { bodyPayload = PayloadMessage (Message "Test" mempty) }
+            , itemLevel = Just LevelInfo
+            , itemPlatform = Just "haskell"
+            , itemLanguage = Just "Haskell"
+            , itemFramework = Just "GHC"
+            , itemRequest = Nothing
+            , itemServer = Nothing
+            , custom = Just mempty
+            , title = Nothing
+            , uuid = Just "12345"
+            , fingerprint = Nothing
+            , itemNotifier = Notifier "rollbar-client" "1.1.0"
+            }
+          jsonItem = decodeUtf8 $ DBL.toStrict $ encode item
+
+      it "omits fields if they are Nothing values" $
+        T.unpack jsonItem `shouldNotContain` "\"server\""
+
+      it "includes fields if they are Just values" $ 
+        T.unpack jsonItem `shouldContain` "\"platform\":\"haskell\""
path: rollbar-client/test/Rollbar/ClientSpec.hs
user: login: DavidMazarro
body: This is missing an empty line at the end of the file. Some external tools rely on files having a linebreak at the end of the file to function properly, so it's best practice to include it (and the GitHub UI warns you like in this case when there is not one). Your editor can be configured to add this on save. For now, do you mind adding it?

<img width="565" alt="Screenshot 2025-03-11 at 11 45 01" src="https://github.com/user-attachments/assets/2edb90db-cd84-42a9-a893-d4c84099fb9e" />

created_at: 2025-03-11T10:46:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -68,4 +69,5 @@ test-suite spec
   build-depends:
       base >=4.13 && <5
     , rollbar-cli
+    , bytestring >=0.10 && <1
path: rollbar-cli/rollbar-cli.cabal
user: login: jmartinez-sb
body: Now that I'm revising it, it should really be 0.12 since it's more recent and compatible with both Haskell 8 and 9
created_at: 2025-03-14T08:12:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -68,4 +69,5 @@ test-suite spec
   build-depends:
       base >=4.13 && <5
     , rollbar-cli
+    , bytestring >=0.10 && <1
path: rollbar-cli/rollbar-cli.cabal
user: login: jmartinez-sb
body: Oh, yes, it's the versions that are being used in the package.yaml file. That must've been the reason for 0.10 as the lower bound and 1 for the upper bound. I'll keep it that way.
created_at: 2025-03-14T08:16:08Z
author_association: CONTRIBUTOR

PR comments:
user: login: jmartinez-sb
created_at: 2025-03-14T08:19:21Z
body: Oh, so the CI failing is something that is expected? Phew, this would've saved me some time trying to solve it, I wish I knew it earlier! 
I've applied you suggestions. Take a look when you have some time.
author_association: CONTRIBUTOR
user: login: DavidMazarro
created_at: 2025-03-14T10:57:48Z
body: Solves #23.
author_association: CONTRIBUTOR