git.iko.soy/typeable/rollbar-hs
on master help sign in
20 files changed+411−274
since splitsnapshots
unifiedsplit
.circleci/config.yml+19−42 .gitignore+1−2 CHANGELOG.md+31 Makefile+35−42 default.nix+21 golden/Item () (': Symbol "Authorization" ('[] Symbol)).faulty.json+6−6 golden/Item () (': Symbol "Authorization" ('[] Symbol)).json+5−5 package.yaml−47 rollbar-hs.cabal+93 shell.nix+44 src/Rollbar/Aeson.hs+22 src/Rollbar/Item.hs+8 src/Rollbar/Item/Body.hs+10−4 src/Rollbar/Item/MissingHeaders.hs+8−3 src/Rollbar/Item/Request.hs+12−2 src/Rollbar/Item/Server.hs+5 stack.yaml+3−63 test/Rollbar/Item/Data/Test.hs+18−10 test/Rollbar/Item/MissingHeaders/Test.hs+41−30 test/Rollbar/Item/Request/Test.hs+29−18
.circleci/config.yml+19−42
@@ -1,63 +1,40 @@
11version: 2
22jobs:
3− compile:
3+ ghc-8.2.2:
44 docker:
5− - image: haskell:8.2.2
5+ - image: nixorg/nix@sha256:96c3f56563a3b9549ac1da12ff7567c39cb6e648afd6a17d4a1a41f0596ad8f6
66 steps:
77 - checkout
88 - restore_cache:
99 keys:
10− - v1-stack-{{ checksum "package.yaml" }}-{{ checksum "stack.yaml" }}
11− - v1-stack-{{ checksum "package.yaml" }}-
12− - v1-stack-
13− name: Restoring stack cache
10+ - v2-nix-{{ checksum "package.yaml" }}
11+ - v2-nix-
12+ name: Restoring cache
1413 - run:
1514 name: Compile
16− command: make build
17− - persist_to_workspace:
18− root: .
19− paths:
20− - .make
21− - dist
15+ command: nix-shell --run 'make sdist test --jobs=2'
16+ environment:
17+ CABAL_BUILD_FLAGS: --jobs=2
2218 - save_cache:
23− key: v1-stack-{{ checksum "package.yaml" }}-{{ checksum "stack.yaml" }}
24− name: Caching stack
19+ key: v2-nix-{{ checksum "package.yaml" }}
20+ name: Caching nix
2521 paths:
26− - .stack-work
27− - /root/.stack
28−
29− sdist:
30− docker:
31− - image: haskell:8.2.2
32− steps:
33− - checkout
34− - restore_cache:
35− keys:
36− - v1-stack-{{ checksum "package.yaml" }}-{{ checksum "stack.yaml" }}
37− - v1-stack-{{ checksum "package.yaml" }}-
38− - v1-stack-
39− name: Restoring stack cache
40− - run:
41− name: Build sdist
42− command: make sdist
22+ - /nix
23+ - dist
4324
44− test:
25+ for-github:
4526 docker:
46− - image: haskell:8.2.2
27+ - image: alpine
4728 steps:
48− - checkout
49− - attach_workspace:
50− at: .
5129 - run:
52− name: Test
53− command: make test
30+ name: Passed
31+ command: echo passed
5432
5533workflows:
5634 version: 2
5735 base:
5836 jobs:
59− - compile
60− - sdist
61− - test:
37+ - for-github:
6238 requires:
63− - compile
39+ - ghc-8.2.2
40+ - ghc-8.2.2
.gitignore+1−2
@@ -1,6 +1,5 @@
11### Project specific ###
22
3−*.cabal
43.make
54bin
65
@@ -186,4 +185,4 @@ $RECYCLE.BIN/
186185*.lnk
187186
188187
189−# End of https://www.gitignore.io/api/vim,osx,linux,macos,emacs,windows,haskell,visualstudiocode
188+# End of https://www.gitignore.io/api/vim,osx,linux,macos,emacs,windows,haskell,visualstudiocode
CHANGELOG.mdnew file+31
@@ -0,0 +1,31 @@
1+# Changelog
2+
3+## 0.3.1.0
4+
5+* [Drop some test dependencies](https://github.com/joneshf/rollbar-hs/pull/12)
6+* [Relax upper bound for `network` to 2.8](https://github.com/joneshf/rollbar-hs/pull/14)
7+
8+## 0.3.0.1
9+
10+* [Add a changelog](https://github.com/joneshf/rollbar-hs/pull/10)
11+
12+## 0.3.0.0
13+
14+* [Bump `aeson` upper bound](https://github.com/joneshf/rollbar-hs/pull/3)
15+
16+## 0.2.0.0
17+
18+* [Fix the type of `Notifier` `name`](https://github.com/joneshf/rollbar-hs/commit/11a3a2463cf44735237d062f15a349d8bfa7ffce)
19+
20+## 0.1.0.1
21+
22+* [Add synopsis](https://github.com/joneshf/rollbar-hs/commit/272c5764a9657f55e00e31c43a3d583362d823d5)
23+
24+## 0.1.0.0
25+
26+* Extract core rollbar to its own package
27+
28+ This is almost entirely copy-pasted from [`wai-middleware-rollbar` version 0.8.4](https://hackage.haskell.org/package/wai-middleware-rollbar-0.8.4).
29+
30+ The intent is to make the core data types available for other packages.
31+ If this gets unweildy, move it right back.
Makefile+35−42
@@ -1,68 +1,61 @@
1−BIN ?= bin
21CABAL ?= cabal
2+CABAL_FLAGS ?=
3+CABAL_BUILD_FLAGS ?=
34DIST ?= dist
4−EMPTY ?= .make
5−PROJECT_NAME ?= rollbar-hs
6−STACK ?= stack
7−STACK_WORK ?= .stack-work
8−VERBOSITY ?= warn
5+GHCID ?= ghcid
6+GHCID_FLAGS ?= --ghc-options=-fno-code
7+HPACK ?= hpack
8+NIX_SHELL ?= nix-shell
9+NIX_SHELL_FLAGS ?=
10+PROJECT_NAME := rollbar-hs
911
1012CABAL_FILE := $(PROJECT_NAME).cabal
13+CONFIGURE := $(DIST)/setup-config
1114DOC_TEST := $(DIST)/build/doc-test/doc-test
12−GHCID := $(BIN)/ghcid
13−STACK_FLAGS := --verbosity $(VERBOSITY)
1415
1516.DEFAULT_GOAL := build
1617
17−$(BIN) $(DIST) $(EMPTY):
18− mkdir -p $@
19−
2018$(CABAL_FILE): package.yaml
21− # `stack` has no way to run `hpack` directly.
22− # We can run `hpack` indirectly with little overhead.
23− $(STACK) $(STACK_FLAGS) build --dry-run
24−
25−$(DOC_TEST): $(EMPTY)/build
26− $@
27−
28−$(EMPTY)/build: $(EMPTY)/stack-setup README.md Setup.hs package.yaml stack.yaml src/**/*.hs test/**/*.hs | $(DIST)
29− $(STACK) $(STACK_FLAGS) build --no-run-tests --test
30− cp -R $$($(STACK) $(STACK_FLAGS) path --dist-dir)/build $(DIST)/build
31− touch $@
32−
33−$(EMPTY)/stack-setup: | $(EMPTY)
34− $(STACK) $(STACK_FLAGS) setup
35− touch $@
19+ $(HPACK)
3620
37−$(GHCID): $(EMPTY)/stack-setup | $(BIN)
38− $(STACK) $(STACK_FLAGS) install ghcid --local-bin-path $(BIN)
21+$(CONFIGURE): $(CABAL_FILE)
22+ $(CABAL) $(CABAL_FLAGS) configure --enable-tests
3923
40−.PHONT: build
41−build: $(EMPTY)/build
24+.PHONY: build
25+build $(DOC_TEST): $(CONFIGURE) default.nix
26+ $(CABAL) $(CABAL_FLAGS) build $(CABAL_BUILD_FLAGS)
4227
43−.PHONY: cabal-check
44−cabal-check: $(CABAL_FILE)
45− $(CABAL) check
28+.PHONY: check
29+check: $(CABAL_FILE)
30+ $(CABAL) $(CABAL_FLAGS) check
4631
4732.PHONY: clean
4833clean:
4934 rm -f $(CABAL_FILE)
50− rm -fr $(BIN)
5135 rm -fr $(DIST)
52− rm -fr $(EMPTY)
53− rm -fr $(STACK_WORK)
36+
37+default.nix: $(CABAL_FILE)
38+ cabal2nix . > $@
5439
5540.PHONY: sdist
56−sdist: cabal-check | $(DIST)
57− $(CABAL) sdist
41+sdist: check
42+ $(CABAL) $(CABAL_FLAGS) sdist
43+
44+.PHONY: shell
45+shell:
46+ $(NIX_SHELL) --pure $(NIX_SHELL_FLAGS)
5847
5948.PHONY: test
60−test: $(DOC_TEST)
49+test: test-doc-test
50+
51+.PHONY: test-doc-test
52+test-doc-test: $(DOC_TEST)
53+ $<
6154
6255.PHONY: upload-hackage
6356upload-hackage: sdist
64− @ $(CABAL) upload $(DIST)/$(PROJECT_NAME)-*.tar.gz
57+ @ $(CABAL) $(CABAL_FLAGS) upload $(DIST)/$(PROJECT_NAME)-*.tar.gz
6558
6659.PHONY: watch
67−watch: $(GHCID)
68− $(GHCID)
60+watch: $(CONFIGURE)
61+ $(GHCID) --command "cabal repl lib:$(PROJECT_NAME) $(GHCID_FLAGS)"
default.nixnew file+21
@@ -0,0 +1,21 @@
1+{ mkDerivation, aeson, base, bytestring, case-insensitive, hostname
2+, hspec, hspec-golden-aeson, http-client, http-conduit, http-types
3+, network, QuickCheck, stdenv, text, time, unordered-containers
4+, uuid
5+}:
6+mkDerivation {
7+ pname = "rollbar-hs";
8+ version = "0.3.1.0";
9+ src = ./.;
10+ libraryHaskellDepends = [
11+ aeson base bytestring case-insensitive hostname http-client
12+ http-conduit http-types network text time unordered-containers uuid
13+ ];
14+ testHaskellDepends = [
15+ aeson base bytestring case-insensitive hspec hspec-golden-aeson
16+ QuickCheck text unordered-containers
17+ ];
18+ homepage = "https://github.com/joneshf/rollbar-hs#readme";
19+ description = "Core Rollbar data types and APIs";
20+ license = stdenv.lib.licenses.bsd3;
21+}
golden/Item () (': Symbol "Authorization" ('[] Symbol)).faulty.json+6−6
@@ -5,7 +5,7 @@
55 "data": {
66 "notifier": {
77 "name": "rollbar-hs",
8− "version": "0.2.0.0"
8+ "version": "0.3.1.0"
99 },
1010 "platform": "linux",
1111 "environment": "HB\u0010BuQ\u0017\u00064d*HE\u000b9\u0019\u001b*\u000e\u0013",
@@ -24,7 +24,7 @@
2424 "data": {
2525 "notifier": {
2626 "name": "rollbar-hs",
27− "version": "0.2.0.0"
27+ "version": "0.3.1.0"
2828 },
2929 "platform": "linux",
3030 "environment": "\u001dGesd@(b)ppm/Z",
@@ -43,7 +43,7 @@
4343 "data": {
4444 "notifier": {
4545 "name": "rollbar-hs",
46− "version": "0.2.0.0"
46+ "version": "0.3.1.0"
4747 },
4848 "platform": "linux",
4949 "environment": "/dt",
@@ -62,7 +62,7 @@
6262 "data": {
6363 "notifier": {
6464 "name": "rollbar-hs",
65− "version": "0.2.0.0"
65+ "version": "0.3.1.0"
6666 },
6767 "platform": "linux",
6868 "environment": "\u0003+(up!I",
@@ -81,7 +81,7 @@
8181 "data": {
8282 "notifier": {
8383 "name": "rollbar-hs",
84− "version": "0.2.0.0"
84+ "version": "0.3.1.0"
8585 },
8686 "platform": "linux",
8787 "environment": "V\u001csj\u001edR`T4?\u000eJ\u000cf",
@@ -97,4 +97,4 @@
9797 "access_token": ".=#\n$\rD4 /1*c *O"
9898 }
9999 ]
100−}
100+}
golden/Item () (': Symbol "Authorization" ('[] Symbol)).json+5−5
@@ -5,7 +5,7 @@
55 "data": {
66 "notifier": {
77 "name": "rollbar-hs",
8− "version": "0.2.0.0"
8+ "version": "0.3.1.0"
99 },
1010 "platform": "linux",
1111 "environment": "HB\u0010BuQ\u0017\u00064d*HE\u000b9\u0019\u001b*\u000e\u0013",
@@ -24,7 +24,7 @@
2424 "data": {
2525 "notifier": {
2626 "name": "rollbar-hs",
27− "version": "0.2.0.0"
27+ "version": "0.3.1.0"
2828 },
2929 "platform": "linux",
3030 "environment": "\u001dGesd@(b)ppm/Z",
@@ -43,7 +43,7 @@
4343 "data": {
4444 "notifier": {
4545 "name": "rollbar-hs",
46− "version": "0.2.0.0"
46+ "version": "0.3.1.0"
4747 },
4848 "platform": "linux",
4949 "environment": "/dt",
@@ -62,7 +62,7 @@
6262 "data": {
6363 "notifier": {
6464 "name": "rollbar-hs",
65− "version": "0.2.0.0"
65+ "version": "0.3.1.0"
6666 },
6767 "platform": "linux",
6868 "environment": "\u0003+(up!I",
@@ -81,7 +81,7 @@
8181 "data": {
8282 "notifier": {
8383 "name": "rollbar-hs",
84− "version": "0.2.0.0"
84+ "version": "0.3.1.0"
8585 },
8686 "platform": "linux",
8787 "environment": "V\u001csj\u001edR`T4?\u000eJ\u000cf",
package.yamldeleted−47
@@ -1,47 +0,0 @@
1−author: Hardy Jones
2−category: Web
3−copyright: 2018 Hardy Jones
4−description: |
5− Provides a type-safe encoding of the Rollbar API.
6− Also provides functions to communicate with the Rollbar API.
7−extra-source-files:
8− - README.md
9−github: joneshf/rollbar-hs
10−library:
11− dependencies:
12− - aeson >= 1.0 && < 1.5
13− - base >= 4.9 && < 5
14− - bytestring >= 0.10 && < 0.11
15− - case-insensitive >= 1.2 && < 1.3
16− - hostname >= 1.0 && < 1.1
17− - http-client >= 0.5 && < 0.6
18− - http-conduit >= 2.2 && < 2.4
19− - http-types >= 0.9 && < 0.13
20− - network >= 2.6 && < 2.7
21− - text >= 1.2 && < 1.3
22− - time >= 1.6 && < 1.9
23− - unordered-containers >= 0.2 && < 0.3
24− - uuid >= 1.3 && < 1.4
25− source-dirs: src
26−license: BSD3
27−maintainer: jones3.hardy@gmail.com
28−name: rollbar-hs
29−synopsis: Core Rollbar data types and APIs.
30−tests:
31− doc-test:
32− dependencies:
33− - QuickCheck >= 2.9
34− - aeson >= 1.0
35− - base >= 4.9
36− - bytestring >= 0.10
37− - case-insensitive >= 1.2
38− - containers >= 0.5
39− - hspec >= 2.4
40− - hspec-golden-aeson >= 0.2
41− - lens >= 4.15
42− - lens-aeson >= 1.0
43− - rollbar-hs
44− - text >= 1.2
45− main: Main.hs
46− source-dirs: test
47−version: '0.2.0.0'
rollbar-hs.cabalnew file+93
@@ -0,0 +1,93 @@
1+cabal-version: 1.12
2+
3+-- This file has been generated from package.yaml by hpack version 0.31.2.
4+--
5+-- see: https://github.com/sol/hpack
6+--
7+-- hash: ac4c551193d60902c95f89512121ee67cb0cafb3a6662796e40323bd0cedff7c
8+
9+name: rollbar-hs
10+version: 0.3.1.0
11+synopsis: Core Rollbar data types and APIs.
12+description: Provides a type-safe encoding of the Rollbar API.
13+ Also provides functions to communicate with the Rollbar API.
14+category: Web
15+homepage: https://github.com/joneshf/rollbar-hs#readme
16+bug-reports: https://github.com/joneshf/rollbar-hs/issues
17+author: Hardy Jones
18+maintainer: jones3.hardy@gmail.com
19+copyright: 2018 Hardy Jones
20+license: BSD3
21+license-file: LICENSE
22+build-type: Simple
23+extra-source-files:
24+ CHANGELOG.md
25+ README.md
26+
27+source-repository head
28+ type: git
29+ location: https://github.com/joneshf/rollbar-hs
30+
31+library
32+ exposed-modules:
33+ Rollbar.AccessToken
34+ Rollbar.API
35+ Rollbar.Item
36+ Rollbar.Item.Body
37+ Rollbar.Item.CodeVersion
38+ Rollbar.Item.Data
39+ Rollbar.Item.Environment
40+ Rollbar.Item.Hardcoded
41+ Rollbar.Item.Internal.Notifier
42+ Rollbar.Item.Internal.Platform
43+ Rollbar.Item.Level
44+ Rollbar.Item.MissingHeaders
45+ Rollbar.Item.Person
46+ Rollbar.Item.Request
47+ Rollbar.Item.Server
48+ Rollbar.Aeson
49+ other-modules:
50+ Paths_rollbar_hs
51+ hs-source-dirs:
52+ src
53+ build-depends:
54+ aeson >=1.1
55+ , base >=4.9
56+ , bytestring >=0.10
57+ , case-insensitive >=1.2
58+ , hostname >=1.0
59+ , http-client >=0.5
60+ , http-conduit >=2.2
61+ , http-types >=0.8.6
62+ , network >=2.6.3
63+ , text >=1.2
64+ , time >=1.6
65+ , unordered-containers >=0.2
66+ , uuid >=1.3
67+ default-language: Haskell2010
68+
69+test-suite doc-test
70+ type: exitcode-stdio-1.0
71+ main-is: Main.hs
72+ other-modules:
73+ Rollbar.Golden
74+ Rollbar.Item.Data.Test
75+ Rollbar.Item.MissingHeaders.Test
76+ Rollbar.Item.Request.Test
77+ Rollbar.QuickCheck
78+ Paths_rollbar_hs
79+ hs-source-dirs:
80+ test
81+ build-depends:
82+ QuickCheck >=2.9
83+ , aeson
84+ , base
85+ , bytestring
86+ , case-insensitive
87+ , hashable
88+ , hspec >=2.4
89+ , hspec-golden-aeson >=0.2
90+ , rollbar-hs
91+ , text
92+ , unordered-containers
93+ default-language: Haskell2010
shell.nixnew file+44
@@ -0,0 +1,44 @@
1+{ compiler ? "ghc822" }:
2+
3+let
4+
5+ default = haskell-packages.callPackage (import ./default.nix) {};
6+
7+ derivation = nixpkgs.haskell.lib.addBuildTools default tools;
8+
9+ haskell-packages = nixpkgs.haskell.packages.${compiler};
10+
11+ nixpkgs = import nixpkgs-tarball {};
12+
13+ nixpkgs-revision = "120b013e0c082d58a5712cde0a7371ae8b25a601";
14+
15+ nixpkgs-sha256 = "0hk4y2vkgm1qadpsm4b0q1vxq889jhxzjx3ragybrlwwg54mzp4f";
16+
17+ nixpkgs-tarball = builtins.fetchTarball {
18+ sha256 = nixpkgs-sha256;
19+ url = "https://github.com/NixOS/nixpkgs/archive/${nixpkgs-revision}.tar.gz";
20+ };
21+
22+ # Tools affected by the compiler version.
23+ tools-compiler = [
24+ haskell-packages.cabal-install
25+ haskell-packages.ghc
26+ haskell-packages.ghcid
27+ haskell-packages.hpack
28+ ];
29+
30+ # Tools that deal with the basic project infrastructure.
31+ tools-infrastructure = [
32+ nixpkgs.cabal2nix
33+ nixpkgs.curl
34+ nixpkgs.which
35+ ];
36+
37+ # All of the tools.
38+ tools =
39+ tools-compiler ++
40+ tools-infrastructure;
41+
42+in
43+
44+ derivation.env
src/Rollbar/Aeson.hsnew file+22
@@ -0,0 +1,22 @@
1+{-# LANGUAGE CPP #-}
2+
3+module Rollbar.Aeson
4+ ( Key
5+ , textToKey
6+ ) where
7+
8+import Data.Text (Text)
9+#if MIN_VERSION_aeson(2,0,0)
10+import Data.Aeson.Key (Key)
11+import qualified Data.Aeson.Key as Key
12+#else
13+#endif
14+
15+#if MIN_VERSION_aeson(2,0,0)
16+textToKey :: Text -> Key
17+textToKey = Key.fromText
18+#else
19+type Key = Text
20+textToKey :: Text -> Key
21+textToKey = id
22+#endif
src/Rollbar/Item.hs+8
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE DataKinds #-}
23{-# LANGUAGE DeriveGeneric #-}
34{-# LANGUAGE NamedFieldPuns #-}
@@ -197,10 +198,17 @@ data Item a headers
197198 }
198199 deriving (Eq, Generic, Show)
199200
201+#if MIN_VERSION_aeson(2, 2, 0)
202+itemKVs
203+ :: (KeyValue e kv, RemoveHeaders headers, ToJSON v)
204+ => Item v headers
205+ -> [kv]
206+#else
200207itemKVs
201208 :: (KeyValue kv, RemoveHeaders headers, ToJSON v)
202209 => Item v headers
203210 -> [kv]
211+#endif
204212itemKVs Item{accessToken, itemData} =
205213 [ "access_token" .= accessToken
206214 , "data" .= itemData
src/Rollbar/Item/Body.hs+10−4
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE DeriveGeneric #-}
23{-# LANGUAGE GeneralizedNewtypeDeriving #-}
34{-# LANGUAGE NamedFieldPuns #-}
@@ -33,7 +34,7 @@ import Data.Aeson
3334 , (.=)
3435 )
3536import Data.Aeson.Encoding (pair)
36−import Data.Aeson.Types (typeMismatch)
37+import Data.Aeson.Types (explicitParseField, typeMismatch)
3738import Data.String (IsString)
3839
3940import GHC.Generics (Generic)
@@ -52,16 +53,21 @@ data Body arbitrary
5253 }
5354 deriving (Eq, Generic, Show)
5455
56+#if MIN_VERSION_aeson(2, 2, 0)
57+bodyKVs :: (KeyValue e kv, ToJSON v) => Body v -> [kv]
58+#else
5559bodyKVs :: (KeyValue kv, ToJSON v) => Body v -> [kv]
60+#endif
5661bodyKVs Message{messageBody, messageData} =
5762 [ "body" .= messageBody
5863 , "data" .= messageData
5964 ]
6065
6166instance FromJSON arbitrary => FromJSON (Body arbitrary) where
62− parseJSON (Object o') = do
63− o <- o' .: "message"
64− Message <$> o .: "body" <*> o .: "data"
67+ parseJSON (Object o') = explicitParseField parseMessage o' "message"
68+ where
69+ parseMessage (Object o) = Message <$> o .: "body" <*> o .: "data"
70+ parseMessage v = typeMismatch "Body arbitrary (message)" v
6571 parseJSON v = typeMismatch "Body arbitrary" v
6672
6773instance ToJSON arbitrary => ToJSON (Body arbitrary) where
src/Rollbar/Item/MissingHeaders.hs+8−3
@@ -3,6 +3,7 @@
33{-# LANGUAGE KindSignatures #-}
44{-# LANGUAGE ScopedTypeVariables #-}
55{-# LANGUAGE TypeOperators #-}
6+{-# LANGUAGE CPP #-}
67
78{-|
89 Module : Rollbar.Item.MissingHeaders
@@ -20,6 +21,7 @@ module Rollbar.Item.MissingHeaders
2021
2122import Data.Aeson
2223 (FromJSON, KeyValue, ToJSON, object, parseJSON, toJSON, (.=))
24+import Rollbar.Aeson
2325import Data.Bifunctor (bimap)
2426import Data.CaseInsensitive (mk, original)
2527import Data.Maybe (catMaybes)
@@ -31,7 +33,6 @@ import Network.HTTP.Types (Header, RequestHeaders)
3133
3234import qualified Data.ByteString as BS
3335import qualified Data.ByteString.Char8 as BSC8
34−import qualified Data.Text as T
3536import qualified Data.Text.Encoding as TE
3637
3738-- | The request headers with some missing
@@ -63,7 +64,11 @@ instance FromJSON (MissingHeaders headers) where
6364instance RemoveHeaders headers => ToJSON (MissingHeaders headers) where
6465 toJSON = object . catMaybes . requestHeadersKVs . removeHeaders
6566
67+#if MIN_VERSION_aeson(2, 2, 0)
68+requestHeadersKVs :: forall e kv. KeyValue e kv => RequestHeaders -> [Maybe kv]
69+#else
6670requestHeadersKVs :: forall kv. KeyValue kv => RequestHeaders -> [Maybe kv]
71+#endif
6772requestHeadersKVs = fmap go
6873 where
6974 go :: Header -> Maybe kv
@@ -72,5 +77,5 @@ requestHeadersKVs = fmap go
7277 val <- myDecodeUtf8 val'
7378 pure (key .= val)
7479
75−myDecodeUtf8 :: BS.ByteString -> Maybe T.Text
76−myDecodeUtf8 = either (const Nothing) Just . TE.decodeUtf8'
80+myDecodeUtf8 :: BS.ByteString -> Maybe Key
81+myDecodeUtf8 = either (const Nothing) (Just . textToKey) . TE.decodeUtf8'
src/Rollbar/Item/Request.hs+12−2
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE DeriveGeneric #-}
23{-# LANGUAGE GeneralizedNewtypeDeriving #-}
34{-# LANGUAGE NamedFieldPuns #-}
@@ -48,6 +49,7 @@ import GHC.Generics (Generic)
4849import Network.HTTP.Types (Query)
4950import Network.Socket (SockAddr(SockAddrInet), tupleToHostAddress)
5051
52+import Rollbar.Aeson
5153import Rollbar.Item.MissingHeaders
5254
5355import Text.Read (readMaybe)
@@ -96,14 +98,18 @@ instance ToJSON Get where
9698 toJSON (Get q) = object . catMaybes . queryKVs $ q
9799 toEncoding (Get q) = pairs . mconcat . catMaybes . queryKVs $ q
98100
99−queryKVs :: forall kv. (KeyValue kv) => Query -> [Maybe kv]
101+#if MIN_VERSION_aeson(2, 2, 0)
102+queryKVs :: forall e kv. KeyValue e kv => Query -> [Maybe kv]
103+#else
104+queryKVs :: forall kv. KeyValue kv => Query -> [Maybe kv]
105+#endif
100106queryKVs = fmap go
101107 where
102108 go :: (BS.ByteString, Maybe BS.ByteString) -> Maybe kv
103109 go (key', val') = do
104110 key <- myDecodeUtf8 key'
105111 let val = val' >>= myDecodeUtf8
106− pure (key .= val)
112+ pure (textToKey key .= val)
107113
108114-- | The HTTP Verb
109115newtype Method
@@ -149,7 +155,11 @@ instance ToJSON IP where
149155 toJSON (IP ip) = toJSON (show ip)
150156 toEncoding (IP ip) = toEncoding (show ip)
151157
158+#if MIN_VERSION_aeson(2, 2, 0)
159+requestKVs :: (KeyValue e kv, RemoveHeaders headers) => Request headers -> [kv]
160+#else
152161requestKVs :: (KeyValue kv, RemoveHeaders headers) => Request headers -> [kv]
162+#endif
153163requestKVs Request{get, headers, method, queryString, rawBody, url, userIP} =
154164 [ "body" .= rawBody
155165 , "GET" .= get
src/Rollbar/Item/Server.hs+5
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE DeriveGeneric #-}
23{-# LANGUAGE GeneralizedNewtypeDeriving #-}
34{-# LANGUAGE NamedFieldPuns #-}
@@ -57,7 +58,11 @@ data Server
5758 }
5859 deriving (Eq, Generic, Show)
5960
61+#if MIN_VERSION_aeson(2, 2, 0)
62+serverKVs :: KeyValue e kv => Server -> [Maybe kv]
63+#else
6064serverKVs :: KeyValue kv => Server -> [Maybe kv]
65+#endif
6166serverKVs Server{branch, host, root, serverCodeVersion} =
6267 [ ("host" .=) <$> host
6368 , ("root" .=) <$> root
stack.yaml+3−63
@@ -1,66 +1,6 @@
1−# This file was automatically generated by 'stack init'
2−#
3−# Some commonly used options have been documented as comments in this file.
4−# For advanced use and comprehensive documentation of the format, please see:
5−# https://docs.haskellstack.org/en/stable/yaml_configuration/
6−
7−# Resolver to choose a 'specific' stackage snapshot or a compiler version.
8−# A snapshot resolver dictates the compiler version and the set of packages
9−# to be used for project dependencies. For example:
10−#
11−# resolver: lts-3.5
12−# resolver: nightly-2015-09-21
13−# resolver: ghc-7.10.2
14−# resolver: ghcjs-0.1.0_ghc-7.10.2
15−# resolver:
16−# name: custom-snapshot
17−# location: "./custom-snapshot.yaml"
18−resolver: lts-10.5
19−
20−# User packages to be built.
21−# Various formats can be used as shown in the example below.
22−#
23−# packages:
24−# - some-directory
25−# - https://example.com/foo/bar/baz-0.0.2.tar.gz
26−# - location:
27−# git: https://github.com/commercialhaskell/stack.git
28−# commit: e7b331f14bcffb8367cd58fbfc8b40ec7642100a
29−# - location: https://github.com/commercialhaskell/stack/commit/e7b331f14bcffb8367cd58fbfc8b40ec7642100a
30−# extra-dep: true
31−# subdirs:
32−# - auto-update
33−# - wai
34−#
35−# A package marked 'extra-dep: true' will only be built if demanded by a
36−# non-dependency (i.e. a user package), and its test suites and benchmarks
37−# will not be run. This is useful for tweaking upstream packages.
1+resolver: lts-23.7
382packages:
393- .
40−# Dependency packages to be pulled from upstream that are not in the resolver
41−# (e.g., acme-missiles-0.3)
42−# extra-deps: []
43−
44−# Override default flag values for local packages and extra-deps
45−# flags: {}
46−
47−# Extra package databases containing global packages
48−# extra-package-dbs: []
494
50−# Control whether we use the GHC we find on the path
51−# system-ghc: true
52−#
53−# Require a specific version of stack, using version ranges
54−# require-stack-version: -any # Default
55−# require-stack-version: ">=1.6"
56−#
57−# Override the architecture used by stack, especially useful on Windows
58−# arch: i386
59−# arch: x86_64
60−#
61−# Extra directories used by stack for building
62−# extra-include-dirs: [/path/to/dir]
63−# extra-lib-dirs: [/path/to/dir]
64−#
65−# Allow a newer minor version of GHC than the snapshot specifies
66−# compiler-check: newer-minor
5+nix:
6+ packages: [ zlib ]
test/Rollbar/Item/Data/Test.hs+18−10
@@ -1,17 +1,18 @@
1−{-# LANGUAGE DataKinds #-}
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE LambdaCase #-}
23{-# LANGUAGE OverloadedStrings #-}
34module Rollbar.Item.Data.Test where
45
5−import Control.Lens ((^@..))
6+import Data.Aeson (Value (Null, Object), decode', encode,
7+ toJSON)
8+import Data.Text (Text)
69
7−import Data.Aeson (encode, toJSON)
8−import Data.Aeson.Lens (key, members)
9−import Data.Text (Text)
10+import Rollbar.Item
11+import Rollbar.QuickCheck ()
1012
11−import Rollbar.Item
12−import Rollbar.QuickCheck ()
13+import Test.QuickCheck (conjoin, quickCheck)
1314
14−import Test.QuickCheck (conjoin, quickCheck)
15+import qualified Data.HashMap.Strict
1516
1617props :: IO ()
1718props =
@@ -24,13 +25,20 @@ prop_valueDataBodyHasRequiredKey :: Data () '["Authorization"] -> Bool
2425prop_valueDataBodyHasRequiredKey x =
2526 length ms == 1 && fst (head ms) `elem` requiredBodyKeys
2627 where
27− ms = toJSON x ^@.. key "body" . members
28+ ms = key "body" (toJSON x)
2829
2930prop_encodingDataBodyHasRequiredKey :: Data () '["Authorization"] -> Bool
3031prop_encodingDataBodyHasRequiredKey x =
3132 length ms == 1 && fst (head ms) `elem` requiredBodyKeys
3233 where
33− ms = encode x ^@.. key "body" . members
34+ ms = foldMap (key "body") (decode' $ encode x)
3435
3536requiredBodyKeys :: [Text]
3637requiredBodyKeys = ["trace", "trace_chain", "message", "crash_report"]
38+
39+key :: Text -> Value -> [(Text, Value)]
40+key k = \case
41+ Object o -> case Data.HashMap.Strict.lookupDefault Null k o of
42+ Object o' -> Data.HashMap.Strict.toList o'
43+ _ -> mempty
44+ _ -> mempty
test/Rollbar/Item/MissingHeaders/Test.hs+41−30
@@ -1,23 +1,26 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
33{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE LambdaCase #-}
45{-# LANGUAGE OverloadedStrings #-}
5−{-# LANGUAGE TypeOperators #-}
6+{-# LANGUAGE TypeOperators #-}
67module Rollbar.Item.MissingHeaders.Test where
78
8−import Control.Lens ((&), (^@..))
9+import Data.Aeson (Value (Object), decode', encode,
10+ toJSON)
11+import Data.Functor (void)
12+import Data.Hashable
13+import Data.HashSet (HashSet)
14+import Data.Text (Text)
915
10−import Data.Aeson (encode, toJSON)
11−import Data.Aeson.Lens (members)
16+import Prelude hiding (error)
1217
13−import Prelude hiding (error)
18+import Rollbar.Item.MissingHeaders (MissingHeaders (..))
19+import Rollbar.QuickCheck ()
1420
15−import Rollbar.Item.MissingHeaders (MissingHeaders(..))
16−import Rollbar.QuickCheck ()
21+import Test.QuickCheck (conjoin, quickCheck)
1722
18−import Test.QuickCheck (conjoin, quickCheck)
19−
20−import Data.Set as S
23+import qualified Data.HashSet
2124
2225props :: IO ()
2326props = do
@@ -38,48 +41,56 @@ props = do
3841
3942prop_valueAuthorizationIsRemoved :: MissingHeaders '["Authorization"] -> Bool
4043prop_valueAuthorizationIsRemoved hs =
41− "Authorization" `S.notMember` actual
44+ "Authorization" `notMember` actual
4245 where
43− actual = toJSON hs ^@.. members & fmap fst & S.fromList
46+ actual = keys (toJSON hs)
4447
4548prop_encodingAuthorizationIsRemoved :: MissingHeaders '["Authorization"] -> Bool
4649prop_encodingAuthorizationIsRemoved hs =
47− "Authorization" `S.notMember` actual
50+ "Authorization" `notMember` actual
4851 where
49− actual = encode hs ^@.. members & fmap fst & S.fromList
52+ actual = foldMap keys (decode' $ encode hs)
5053
5154prop_valueX_AccessTokenIsRemoved :: MissingHeaders '["X-AccessToken"] -> Bool
5255prop_valueX_AccessTokenIsRemoved hs =
53− "X-AccessToken" `S.notMember` actual
56+ "X-AccessToken" `notMember` actual
5457 where
55− actual = toJSON hs ^@.. members & fmap fst & S.fromList
58+ actual = keys (toJSON hs)
5659
5760prop_encodingX_AccessTokenIsRemoved :: MissingHeaders '["X-AccessToken"] -> Bool
5861prop_encodingX_AccessTokenIsRemoved hs =
59− "X-AccessToken" `S.notMember` actual
62+ "X-AccessToken" `notMember` actual
6063 where
61− actual = encode hs ^@.. members & fmap fst & S.fromList
64+ actual = foldMap keys (decode' $ encode hs)
6265
6366prop_valueAllHeadersAreRemoved
6467 :: MissingHeaders
6568 '["Authorization", "this is made up", "Server", "X-AccessToken"]
6669 -> Bool
6770prop_valueAllHeadersAreRemoved hs =
68− "Authorization" `S.notMember` actual
69− && "this is made up" `S.notMember` actual
70− && "Server" `S.notMember` actual
71− && "X-AccessToken" `S.notMember` actual
71+ "Authorization" `notMember` actual
72+ && "this is made up" `notMember` actual
73+ && "Server" `notMember` actual
74+ && "X-AccessToken" `notMember` actual
7275 where
73− actual = toJSON hs ^@.. members & fmap fst & S.fromList
76+ actual = keys (toJSON hs)
7477
7578prop_encodingAllHeadersAreRemoved
7679 :: MissingHeaders
7780 '["Authorization", "this is made up", "Server", "X-AccessToken"]
7881 -> Bool
7982prop_encodingAllHeadersAreRemoved hs =
80− "Authorization" `S.notMember` actual
81− && "this is made up" `S.notMember` actual
82− && "Server" `S.notMember` actual
83− && "X-AccessToken" `S.notMember` actual
83+ "Authorization" `notMember` actual
84+ && "this is made up" `notMember` actual
85+ && "Server" `notMember` actual
86+ && "X-AccessToken" `notMember` actual
8487 where
85− actual = encode hs ^@.. members & fmap fst & S.fromList
88+ actual = foldMap keys (decode' $ encode hs)
89+
90+keys :: Value -> HashSet Text
91+keys = \case
92+ Object o -> Data.HashSet.fromMap (void o)
93+ _ -> mempty
94+
95+notMember :: (Eq a, Hashable a) => a -> HashSet a -> Bool
96+notMember x = not . Data.HashSet.member x
test/Rollbar/Item/Request/Test.hs+29−18
@@ -1,24 +1,27 @@
1−{-# LANGUAGE DataKinds #-}
1+{-# LANGUAGE DataKinds #-}
22{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE LambdaCase #-}
34{-# LANGUAGE OverloadedStrings #-}
4−{-# LANGUAGE TypeOperators #-}
5+{-# LANGUAGE TypeOperators #-}
56module Rollbar.Item.Request.Test where
67
7−import Control.Lens ((&), (^@..))
8+import Data.Aeson (Value (Object), decode', encode, toJSON)
9+import Data.CaseInsensitive (original)
10+import Data.Foldable (fold)
11+import Data.Functor (void)
12+import Data.HashSet (HashSet)
13+import Data.Text (Text)
814
9−import Data.Aeson (encode, toJSON)
10−import Data.Aeson.Lens (members)
11−import Data.CaseInsensitive (original)
15+import Prelude hiding (error)
1216
13−import Prelude hiding (error)
17+import Rollbar.Item.Request (MissingHeaders (..))
18+import Rollbar.QuickCheck ()
1419
15−import Rollbar.Item.Request (MissingHeaders(..))
16−import Rollbar.QuickCheck ()
20+import Test.QuickCheck (conjoin, quickCheck)
1721
18−import Test.QuickCheck (conjoin, quickCheck)
22+import qualified Data.Text.Encoding as TE
1923
20−import Data.Set as S
21−import Data.Text.Encoding as TE
24+import qualified Data.HashSet
2225
2326props :: IO ()
2427props =
@@ -29,14 +32,22 @@ props =
2932
3033prop_valueHeadersArentWrapped :: MissingHeaders '["Authorization"] -> Bool
3134prop_valueHeadersArentWrapped hs@(MissingHeaders rhs) =
32− actual `S.isSubsetOf` expected
35+ actual `isSubsetOf` expected
3336 where
34− actual = toJSON hs ^@.. members & fmap fst & S.fromList
35− expected = S.fromList $ either (const "") id . TE.decodeUtf8' . original . fst <$> rhs
37+ actual = keys (toJSON hs)
38+ expected = Data.HashSet.fromList $ fold . TE.decodeUtf8' . original . fst <$> rhs
39+ isSubsetOf x y = Data.HashSet.difference x y == mempty
3640
3741prop_encodingHeadersArentWrapped :: MissingHeaders '["Authorization"] -> Bool
3842prop_encodingHeadersArentWrapped hs@(MissingHeaders rhs) =
39− actual `S.isSubsetOf` expected
43+ actual `isSubsetOf` expected
4044 where
41− actual = encode hs ^@.. members & fmap fst & S.fromList
42− expected = S.fromList $ either (const "") id . TE.decodeUtf8' . original . fst <$> rhs
45+ actual = foldMap keys (decode' $ encode hs)
46+ expected = Data.HashSet.fromList $ fold . TE.decodeUtf8' . original . fst <$> rhs
47+ isSubsetOf x y = Data.HashSet.difference x y == mempty
48+
49+keys :: Value -> HashSet Text
50+keys = \case
51+ Object o -> Data.HashSet.fromMap (void o)
52+ _ -> mempty
53+