json-spec-elm-servant 0.6.0.0 → 2.0.0.0
raw patch · 4 files changed
+185/−130 lines, 4 filesdep ~aesondep ~json-specdep ~json-spec-elmPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson, json-spec, json-spec-elm
API changes (from Hackage documentation)
- Data.JsonSpec.Elm.Servant: instance Data.JsonSpec.Elm.HasType (Data.JsonSpec.Decode.DecodingSpec a) => Data.JsonSpec.Elm.Servant.IsParam (Servant.API.ReqBody.ReqBody' (Servant.API.Modifiers.Required : mods) (Servant.API.ContentTypes.JSON : accept) a)
- Data.JsonSpec.Elm.Servant: instance forall k1 response (method :: k1) (code :: GHC.Internal.TypeNats.Nat) (types :: [*]). (Data.JsonSpec.Elm.HasType (Data.JsonSpec.Encode.EncodingSpec response), Servant.API.Verbs.ReflectMethod method) => Data.JsonSpec.Elm.Servant.Elmable (Servant.API.Verbs.Verb method code types response)
+ Data.JsonSpec.Elm.Servant: instance Data.JsonSpec.Elm.HasType ('Data.JsonSpec.Spec.JsonModule (Data.JsonSpec.Spec.DecodingSpec a)) => Data.JsonSpec.Elm.Servant.IsParam (Servant.API.ReqBody.ReqBody' (Servant.API.Modifiers.Required : mods) (Servant.API.ContentTypes.JSON : accept) a)
+ Data.JsonSpec.Elm.Servant: instance forall k1 response (method :: k1) (code :: GHC.Internal.TypeNats.Nat) (types :: [*]). (Data.JsonSpec.Elm.HasType ('Data.JsonSpec.Spec.JsonModule (Data.JsonSpec.Spec.EncodingSpec response)), Servant.API.Verbs.ReflectMethod method) => Data.JsonSpec.Elm.Servant.Elmable (Servant.API.Verbs.Verb method code types response)
Files
- changelog.md +11/−0
- json-spec-elm-servant.cabal +4/−4
- src/Data/JsonSpec/Elm/Servant.hs +7/−6
- test/Api.hs +163/−120
changelog.md view
@@ -1,5 +1,16 @@ # Changelog +## 2.0.0.0++- Require `json-spec` >= 2.0 and adapt to its 2.0 API (`'Module` /+ `JsonModule`, `SpecJson`, `TupleDecoding`, and `TupleEncoding`).+- The major (epoch) version is bumped to 2.0 to indicate compatibility+ with the `json-spec` 2.0 major/epoch version.++## 0.6.0.1++- Relaxed the upper bound on `aeson` to allow version 2.3.+ ## 0.6.0.0 - Require `json-spec` >= 1.4 and `json-spec-elm` >= 0.6, which provide
json-spec-elm-servant.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: json-spec-elm-servant-version: 0.6.0.0+version: 2.0.0.0 synopsis: Generated elm code for servant APIs. description: Generate Elm encoders, decoders, and API requests for an Servant API, where the shape of the data@@ -31,8 +31,8 @@ , elm-syntax >= 0.3.3.0 && < 0.4 , filepath >= 1.4.200.1 && < 1.6 , http-types >= 0.12.3 && < 0.13- , json-spec >= 1.4.0.0 && < 1.5- , json-spec-elm >= 0.6.0.0 && < 0.7+ , json-spec >= 2.0.0.0 && < 2.1+ , json-spec-elm >= 2.0.0.0 && < 2.1 , mtl >= 2.3.1 && < 2.4 , prettyprinter >= 1.7.1 && < 1.8 , process >= 1.6.18.0 && < 1.7@@ -73,7 +73,7 @@ Api build-depends: , json-spec-elm-servant- , aeson >= 2.2.1.0 && < 2.3+ , aeson >= 2.2.1.0 && < 2.4 , binary >= 0.8.9.1 && < 0.9 , bytestring >= 0.12.0.2 && < 0.13 , cookie >= 0.4.6 && < 0.6
src/Data/JsonSpec/Elm/Servant.hs view
@@ -43,6 +43,7 @@ import Data.HashMap.Strict (HashMap) import Data.JsonSpec ( HasJsonDecodingSpec(DecodingSpec), HasJsonEncodingSpec(EncodingSpec)+ , Specification(JsonModule) ) import Data.JsonSpec.Elm (HasType(decoderOf, encoderOf, typeOf), Definitions) import Data.Maybe (fromJust, fromMaybe, mapMaybe)@@ -393,15 +394,15 @@ endpoints = endpoints @(NoContentVerb m) instance {- Elmable (Verb method code types response) -} {-# overlaps #-}- ( HasType (EncodingSpec response)+ ( HasType (JsonModule (EncodingSpec response)) , ReflectMethod method ) => Elmable (Verb method code types response) where endpoints (reverse -> params) = do- responseType <- typeOf @(EncodingSpec response)- decoder <- decoderOf @(EncodingSpec response)+ responseType <- typeOf @(JsonModule (EncodingSpec response))+ decoder <- decoderOf @(JsonModule (EncodingSpec response)) tell . Set.singleton $ Def.Constant (requestFunctionName @method params)@@ -459,13 +460,13 @@ where param = param @(Header' mods name a) instance {- IsParam (ReqBody' (Required : mods) (JSON : accept) a) -}- (HasType (DecodingSpec a))+ (HasType (JsonModule (DecodingSpec a))) => IsParam (ReqBody' (Required : mods) (JSON : accept) a) where param = do- elmType <- typeOf @(DecodingSpec a)- encoder <- encoderOf @(DecodingSpec a)+ elmType <- typeOf @(JsonModule (DecodingSpec a))+ encoder <- encoderOf @(JsonModule (DecodingSpec a)) pure $ BodyEncoder {elmType, encoder} instance {- IsParam (ReqBody' (other : mods) (JSON : accept) a) -} {-# overlaps #-} (IsParam (ReqBody' (other : mods) accept a))
test/Api.hs view
@@ -44,19 +44,25 @@ SetMetadataReq(..), KV(..), FEConfig(..),+ Guilds(..), ) where import Data.Aeson (FromJSON, FromJSONKey, ToJSON, ToJSONKey) import Data.Binary (Binary) import Data.ByteString (ByteString) import Data.JsonSpec- ( Field(Field), HasJsonDecodingSpec(DecodingSpec, fromJSONStructure)- , HasJsonEncodingSpec(EncodingSpec, toJSONStructure), SpecJSON(SpecJSON)+ ( HasJsonDecodingSpec(DecodingSpec), HasJsonEncodingSpec(EncodingSpec)+ , Module(Module) , Specification ( JsonArray, JsonDateTime, JsonDict, JsonEither, JsonInt, JsonLet- , JsonObject, JsonRef, JsonString, JsonTag+ , JsonModule, JsonObject, JsonRef, JsonString, JsonTag )+ , type (:::), type (::?), type (:=) )+import Data.JsonSpec.Codec.Tuple+ ( Field(Field), SpecJson(SpecJson), TupleDecoding(fromJsonStructure)+ , TupleEncoding(toJsonStructure)+ ) import Data.Map (Map) import Data.Set (Set) import Data.Text (Text)@@ -75,7 +81,6 @@ , Verb ) import Web.Cookie (SetCookie)-import qualified Data.JsonSpec as Spec data Api mode = Api { protectedApi :: mode@@ -146,32 +151,45 @@ , getGuilds :: mode :- "guilds"- :> Get '[JSON] (Set Guild)+ :> Get '[JSON] Guilds } deriving stock (Generic) +newtype Guilds = Guilds+ { unGuilds :: Set Guild+ }+ deriving ToJSON via (SpecJson Guilds)+instance HasJsonEncodingSpec Guilds where+ type EncodingSpec Guilds =+ 'Module (JsonArray (JsonModule (EncodingSpec Guild)))+instance TupleEncoding Guilds where+ toJsonStructure = undefined++ data SetMetadataReq = SetMetadataReq { name :: Name , description :: Text , venue :: Text }- deriving FromJSON via (SpecJSON SetMetadataReq)+ deriving FromJSON via (SpecJson SetMetadataReq) instance HasJsonDecodingSpec SetMetadataReq where type DecodingSpec SetMetadataReq =- JsonObject- '[ "name" ::: (DecodingSpec Name)- , "description" ::: JsonString- , "venue" ::: JsonString- ]- fromJSONStructure+ 'Module+ (JsonObject+ '[ "name" ::: JsonModule (DecodingSpec Name)+ , "description" ::: JsonString+ , "venue" ::: JsonString+ ])+instance TupleDecoding SetMetadataReq where+ fromJsonStructure (Field @"name" name_, (Field @"description" description, (Field @"venue" venue, ()))) = do- name <- fromJSONStructure name_+ name <- fromJsonStructure name_ pure SetMetadataReq { name@@ -185,20 +203,21 @@ , credits :: AvailableCredits , user :: DiscordUser }- deriving ToJSON via (SpecJSON DashboardData)+ deriving ToJSON via (SpecJson DashboardData) instance HasJsonEncodingSpec DashboardData where type EncodingSpec DashboardData =- JsonLet- '[ '("DashboardData"- , JsonObject- '[ "proposals" ::: JsonDict (EncodingSpec Proposal)- , "credits" ::: EncodingSpec AvailableCredits- , "user" ::: EncodingSpec DiscordUser- ]- )- ]- (JsonRef "DashboardData")- toJSONStructure = undefined+ 'Module+ (JsonLet+ '[ "DashboardData" :=+ JsonObject+ '[ "proposals" ::: JsonDict (JsonModule (EncodingSpec Proposal))+ , "credits" ::: JsonModule (EncodingSpec AvailableCredits)+ , "user" ::: JsonModule (EncodingSpec DiscordUser)+ ]+ ]+ (JsonRef "DashboardData"))+instance TupleEncoding DashboardData where+ toJsonStructure = undefined data Proposal = Proposal@@ -211,50 +230,55 @@ , createdAt :: UTCTime } deriving stock (Generic)- deriving (ToJSON, FromJSON) via (SpecJSON Proposal)+ deriving (ToJSON, FromJSON) via (SpecJson Proposal) instance HasJsonEncodingSpec Proposal where type EncodingSpec Proposal =- JsonObject- '[ "name" ::: EncodingSpec Name- , "owner" ::: EncodingSpec DiscordUser- , "availability" ::: JsonArray (EncodingSpec AvailabilityInterval)- , "description" ::: JsonString- , "venue" ::: JsonString- , "invites" ::: JsonArray (EncodingSpec Invite)- , "created-at" ::: JsonDateTime- ]- toJSONStructure = undefined+ 'Module+ (JsonObject+ '[ "name" ::: JsonModule (EncodingSpec Name)+ , "owner" ::: JsonModule (EncodingSpec DiscordUser)+ , "availability" ::: JsonArray (JsonModule (EncodingSpec AvailabilityInterval))+ , "description" ::: JsonString+ , "venue" ::: JsonString+ , "invites" ::: JsonArray (JsonModule (EncodingSpec Invite))+ , "created-at" ::: JsonDateTime+ ])+instance TupleEncoding Proposal where+ toJsonStructure = undefined instance HasJsonDecodingSpec Proposal where type DecodingSpec Proposal = EncodingSpec Proposal- fromJSONStructure = undefined+instance TupleDecoding Proposal where+ fromJsonStructure = undefined data Invite = InviteUser DiscordUser | InviteGuild Guild deriving stock (Eq, Ord)- deriving (ToJSON, FromJSON) via (SpecJSON Invite)+ deriving (ToJSON, FromJSON) via (SpecJson Invite) instance HasJsonEncodingSpec Invite where type EncodingSpec Invite =- JsonLet- '[ '( "Invite"- , JsonEither- '[ JsonObject+ 'Module+ (JsonLet+ '[ "Invite" :=+ JsonEither+ '[ JsonObject '[ "type" ::: JsonTag "discord-user"- , "username" ::: EncodingSpec DiscordUser+ , "username" ::: JsonModule (EncodingSpec DiscordUser) ] , JsonObject '[ "type" ::: JsonTag "discord-server"- , "guild" ::: EncodingSpec Guild+ , "guild" ::: JsonModule (EncodingSpec Guild) ] ]- )- ]- (JsonRef "Invite")- toJSONStructure = undefined+ ]+ (JsonRef "Invite"))+instance TupleEncoding Invite where+ toJsonStructure = undefined instance HasJsonDecodingSpec Invite where type DecodingSpec Invite = EncodingSpec Invite- fromJSONStructure = undefined+instance TupleDecoding Invite where+ fromJsonStructure = undefined data Guild = Guild@@ -262,56 +286,65 @@ , name :: Text } deriving stock (Eq, Ord)- deriving (ToJSON, FromJSON) via (SpecJSON Guild)+ deriving (ToJSON, FromJSON) via (SpecJson Guild) instance HasJsonEncodingSpec Guild where type EncodingSpec Guild =- JsonObject- '[ "id" ::: EncodingSpec GuildId- , "name" ::: JsonString- ]- toJSONStructure = undefined+ 'Module+ (JsonObject+ '[ "id" ::: JsonModule (EncodingSpec GuildId)+ , "name" ::: JsonString+ ])+instance TupleEncoding Guild where+ toJsonStructure = undefined instance HasJsonDecodingSpec Guild where type DecodingSpec Guild = EncodingSpec Guild- fromJSONStructure = undefined+instance TupleDecoding Guild where+ fromJsonStructure = undefined newtype GuildId = GuildId { unGuildId :: Text } deriving newtype (ToHttpApiData, Eq, Ord)- deriving FromJSON via (SpecJSON GuildId)+ deriving FromJSON via (SpecJson GuildId) instance HasJsonEncodingSpec GuildId where- type EncodingSpec GuildId = JsonString- toJSONStructure = unGuildId+ type EncodingSpec GuildId = 'Module JsonString+instance TupleEncoding GuildId where+ toJsonStructure = unGuildId instance HasJsonDecodingSpec GuildId where type DecodingSpec GuildId = EncodingSpec GuildId- fromJSONStructure = pure . GuildId+instance TupleDecoding GuildId where+ fromJsonStructure = pure . GuildId data AvailabilityInterval = AvailabilityInterval { interval :: Interval , users :: Set DiscordUser }- deriving ToJSON via (SpecJSON AvailabilityInterval)+ deriving ToJSON via (SpecJson AvailabilityInterval) instance HasJsonDecodingSpec AvailabilityInterval where type DecodingSpec AvailabilityInterval = EncodingSpec AvailabilityInterval- fromJSONStructure = undefined+instance TupleDecoding AvailabilityInterval where+ fromJsonStructure = undefined instance HasJsonEncodingSpec AvailabilityInterval where type EncodingSpec AvailabilityInterval =- JsonObject- '[ "interval" ::: EncodingSpec Interval- , "users" ::: JsonArray (EncodingSpec DiscordUser)- ]- toJSONStructure = undefined+ 'Module+ (JsonObject+ '[ "interval" ::: JsonModule (EncodingSpec Interval)+ , "users" ::: JsonArray (JsonModule (EncodingSpec DiscordUser))+ ])+instance TupleEncoding AvailabilityInterval where+ toJsonStructure = undefined newtype AvailableCredits = AvailableCredits { unAvailableCredits :: Int }- deriving ToJSON via (SpecJSON AvailableCredits)+ deriving ToJSON via (SpecJson AvailableCredits) instance HasJsonEncodingSpec AvailableCredits where- type EncodingSpec AvailableCredits = JsonInt- toJSONStructure = undefined+ type EncodingSpec AvailableCredits = 'Module JsonInt+instance TupleEncoding AvailableCredits where+ toJsonStructure = undefined data UnprotectedApi mode = UnprotectedApi@@ -347,12 +380,14 @@ newtype FEConfig = FEConfig { discordRedirect :: Text }- deriving (ToJSON) via (SpecJSON FEConfig)+ deriving (ToJSON) via (SpecJson FEConfig) instance HasJsonEncodingSpec FEConfig where type EncodingSpec FEConfig =- JsonObject- '[ "redirectUrl" ::: JsonString ]- toJSONStructure = undefined+ 'Module+ (JsonObject+ '[ "redirectUrl" ::: JsonString ])+instance TupleEncoding FEConfig where+ toJsonStructure = undefined newtype Email = Email { unEmail :: Text@@ -364,10 +399,11 @@ { unDiscordAccessToken :: Text } deriving newtype (Binary)- deriving (FromJSON) via (SpecJSON DiscordAccessToken)+ deriving (FromJSON) via (SpecJson DiscordAccessToken) instance HasJsonDecodingSpec DiscordAccessToken where- type DecodingSpec DiscordAccessToken = JsonString- fromJSONStructure = undefined+ type DecodingSpec DiscordAccessToken = 'Module JsonString+instance TupleDecoding DiscordAccessToken where+ fromJsonStructure = undefined newtype Token = Token@@ -390,10 +426,11 @@ , ToJSONKey , FromJSONKey )- deriving ToJSON via (SpecJSON ProposalId)+ deriving ToJSON via (SpecJson ProposalId) instance HasJsonEncodingSpec ProposalId where- type EncodingSpec ProposalId = JsonString- toJSONStructure = undefined+ type EncodingSpec ProposalId = 'Module JsonString+instance TupleEncoding ProposalId where+ toJsonStructure = undefined data NewProposalReq = NewProposalReq@@ -402,37 +439,43 @@ , description :: Text , venue :: Maybe Text }- deriving (FromJSON) via (SpecJSON NewProposalReq)+ deriving (FromJSON) via (SpecJson NewProposalReq) instance HasJsonDecodingSpec NewProposalReq where type DecodingSpec NewProposalReq =- JsonObject- '[ "name" ::: DecodingSpec Name- , "availability" ::: DecodingSpec Availability- , "description" ::: JsonString- , "venue" ::? JsonString- ]- fromJSONStructure = undefined+ 'Module+ (JsonObject+ '[ "name" ::: JsonModule (DecodingSpec Name)+ , "availability" ::: JsonModule (DecodingSpec Availability)+ , "description" ::: JsonString+ , "venue" ::? JsonString+ ])+instance TupleDecoding NewProposalReq where+ fromJsonStructure = undefined newtype Availability = Availability { unAvailability :: Set Interval }- deriving FromJSON via (SpecJSON Availability)+ deriving FromJSON via (SpecJson Availability) instance HasJsonDecodingSpec Availability where- type DecodingSpec Availability = JsonArray (DecodingSpec Interval)- fromJSONStructure = undefined+ type DecodingSpec Availability =+ 'Module (JsonArray (JsonModule (DecodingSpec Interval)))+instance TupleDecoding Availability where+ fromJsonStructure = undefined newtype Name = Name { unName :: Text }- deriving (ToJSON, FromJSON) via (SpecJSON Name)+ deriving (ToJSON, FromJSON) via (SpecJson Name) instance HasJsonEncodingSpec Name where- type EncodingSpec Name = JsonString- toJSONStructure = undefined+ type EncodingSpec Name = 'Module JsonString+instance TupleEncoding Name where+ toJsonStructure = undefined instance HasJsonDecodingSpec Name where type DecodingSpec Name = EncodingSpec Name- fromJSONStructure = undefined+instance TupleDecoding Name where+ fromJsonStructure = undefined data Interval = Interval@@ -440,49 +483,49 @@ , endExclusive :: UTCTime } deriving stock (Eq, Ord)- deriving (ToJSON, FromJSON) via (SpecJSON Interval)+ deriving (ToJSON, FromJSON) via (SpecJson Interval) instance HasJsonEncodingSpec Interval where type EncodingSpec Interval =- JsonObject- '[ "startInclusive" ::: JsonDateTime- , "endExclusive" ::: JsonDateTime- ]- toJSONStructure = undefined+ 'Module+ (JsonObject+ '[ "startInclusive" ::: JsonDateTime+ , "endExclusive" ::: JsonDateTime+ ])+instance TupleEncoding Interval where+ toJsonStructure = undefined instance HasJsonDecodingSpec Interval where type DecodingSpec Interval = EncodingSpec Interval- fromJSONStructure = undefined+instance TupleDecoding Interval where+ fromJsonStructure = undefined newtype DiscordUser = DiscordUser { unDiscordUser :: Text } deriving newtype ( Eq , FromJSON , Ord, Binary)- deriving ToJSON via (SpecJSON DiscordUser)+ deriving ToJSON via (SpecJson DiscordUser) instance HasJsonEncodingSpec DiscordUser where- type EncodingSpec DiscordUser = JsonString- toJSONStructure = undefined+ type EncodingSpec DiscordUser = 'Module JsonString+instance TupleEncoding DiscordUser where+ toJsonStructure = undefined instance HasJsonDecodingSpec DiscordUser where type DecodingSpec DiscordUser = EncodingSpec DiscordUser- fromJSONStructure = undefined+instance TupleDecoding DiscordUser where+ fromJsonStructure = undefined data KV k v = KV { key :: k , value :: v }-deriving via (SpecJSON (KV ProposalId Proposal)) instance+deriving via (SpecJson (KV ProposalId Proposal)) instance ToJSON (KV ProposalId Proposal) instance HasJsonEncodingSpec (KV k v) where- type EncodingSpec (KV k v) =- JsonObject- '[ "key" ::: EncodingSpec k- , "value" ::: EncodingSpec v- ]-- toJSONStructure = undefined---type (:::) = Spec.Required-type (::?) = Spec.Optional--+ type EncodingSpec (KV k v) =+ 'Module+ (JsonObject+ '[ "key" ::: JsonModule (EncodingSpec k)+ , "value" ::: JsonModule (EncodingSpec v)+ ])+instance TupleEncoding (KV k v) where+ toJsonStructure = undefined