Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: decode and validate all optionals. #764

Merged
merged 1 commit into from
Sep 23, 2024
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
12 changes: 12 additions & 0 deletions src/Data/Common/DecodeUtils.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Data.Common.DecodeUtils exposing (strictOptional)

import Json.Decode exposing (Decoder)
import Json.Decode.Extra as DE


{-| A stricter Decode.maybe using Json.Decode.Extra's optionalField here because we want
a failure when a Maybe decoded field value is invalid.
-}
strictOptional : String -> Decoder a -> Decoder (Maybe a -> b) -> Decoder b
strictOptional field decoder =
DE.andMap (DE.optionalField field decoder)
5 changes: 3 additions & 2 deletions src/Data/Food/Process.elm
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module Data.Food.Process exposing
, nameToString
)

import Data.Common.DecodeUtils as DU
import Data.Impact as Impact exposing (Impacts)
import Json.Decode as Decode exposing (Decoder)
import Json.Decode.Extra as DE
Expand Down Expand Up @@ -184,8 +185,8 @@ decodeProcess : Decoder Impact.Impacts -> Decoder Process
decodeProcess impactsDecoder =
Decode.succeed Process
|> Pipe.required "category" decodeCategory
|> Pipe.optional "comment" (Decode.maybe Decode.string) Nothing
|> Pipe.optional "displayName" (Decode.maybe Decode.string) Nothing
|> DU.strictOptional "comment" Decode.string
|> DU.strictOptional "displayName" Decode.string
|> Pipe.required "id" Decode.string
|> Pipe.required "identifier" decodeIdentifier
|> Pipe.required "impacts" impactsDecoder
Expand Down
7 changes: 4 additions & 3 deletions src/Data/Food/Query.elm
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module Data.Food.Query exposing
)

import Base64
import Data.Common.DecodeUtils as DU
import Data.Country as Country
import Data.Food.Ingredient as Ingredient
import Data.Food.Preparation as Preparation
Expand Down Expand Up @@ -102,11 +103,11 @@ buildApiQuery clientUrl query =
decode : Decoder Query
decode =
Decode.succeed Query
|> Pipe.optional "distribution" (Decode.maybe Retail.decode) Nothing
|> DU.strictOptional "distribution" Retail.decode
|> Pipe.required "ingredients" (Decode.list decodeIngredient)
|> Pipe.optional "packaging" (Decode.list decodeProcess) []
|> Pipe.optional "preparation" (Decode.list Preparation.decodeId) []
|> Pipe.optional "transform" (Decode.maybe decodeProcess) Nothing
|> DU.strictOptional "transform" decodeProcess


decodePlaneTransport : Decoder Ingredient.PlaneTransport
Expand Down Expand Up @@ -153,7 +154,7 @@ decodeProcess =
decodeIngredient : Decoder IngredientQuery
decodeIngredient =
Decode.succeed IngredientQuery
|> Pipe.optional "country" (Decode.maybe Country.decodeCode) Nothing
|> DU.strictOptional "country" Country.decodeCode
|> Pipe.required "id" Ingredient.decodeId
|> Pipe.required "mass" decodeMassInGrams
|> Pipe.optional "byPlane" decodePlaneTransport Ingredient.PlaneNotApplicable
Expand Down
51 changes: 22 additions & 29 deletions src/Data/Textile/Query.elm
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module Data.Textile.Query exposing
)

import Base64
import Data.Common.DecodeUtils as DU
import Data.Country as Country
import Data.Split as Split exposing (Split)
import Data.Textile.DyeingMedium as DyeingMedium exposing (DyeingMedium)
Expand All @@ -37,7 +38,6 @@ import Data.Textile.Product as Product exposing (Product)
import Data.Textile.Step.Label as Label exposing (Label)
import Data.Unit as Unit
import Json.Decode as Decode exposing (Decoder)
import Json.Decode.Extra as DE
import Json.Decode.Pipeline as Pipe
import Json.Encode as Encode
import List.Extra as LE
Expand Down Expand Up @@ -111,46 +111,39 @@ buildApiQuery clientUrl query =
decode : Decoder Query
decode =
Decode.succeed Query
|> strictOptional "airTransportRatio" Split.decodeFloat
|> strictOptional "business" Economics.decodeBusiness
|> strictOptional "countryDyeing" Country.decodeCode
|> strictOptional "countryFabric" Country.decodeCode
|> strictOptional "countryMaking" Country.decodeCode
|> strictOptional "countrySpinning" Country.decodeCode
|> DU.strictOptional "airTransportRatio" Split.decodeFloat
|> DU.strictOptional "business" Economics.decodeBusiness
|> DU.strictOptional "countryDyeing" Country.decodeCode
|> DU.strictOptional "countryFabric" Country.decodeCode
|> DU.strictOptional "countryMaking" Country.decodeCode
|> DU.strictOptional "countrySpinning" Country.decodeCode
|> Pipe.optional "disabledSteps" (Decode.list Label.decodeFromCode) []
|> strictOptional "dyeingMedium" DyeingMedium.decode
|> strictOptional "fabricProcess" Fabric.decode
|> strictOptional "fading" Decode.bool
|> strictOptional "makingComplexity" MakingComplexity.decode
|> strictOptional "makingDeadStock" Split.decodeFloat
|> strictOptional "makingWaste" Split.decodeFloat
|> DU.strictOptional "dyeingMedium" DyeingMedium.decode
|> DU.strictOptional "fabricProcess" Fabric.decode
|> DU.strictOptional "fading" Decode.bool
|> DU.strictOptional "makingComplexity" MakingComplexity.decode
|> DU.strictOptional "makingDeadStock" Split.decodeFloat
|> DU.strictOptional "makingWaste" Split.decodeFloat
|> Pipe.required "mass" (Decode.map Mass.kilograms Decode.float)
|> Pipe.required "materials" (Decode.list decodeMaterialQuery)
|> strictOptional "numberOfReferences" Decode.int
|> strictOptional "physicalDurability" Unit.decodePhysicalDurability
|> strictOptional "price" Economics.decodePrice
|> strictOptional "printing" Printing.decode
|> DU.strictOptional "numberOfReferences" Decode.int
|> DU.strictOptional "physicalDurability" Unit.decodePhysicalDurability
|> DU.strictOptional "price" Economics.decodePrice
|> DU.strictOptional "printing" Printing.decode
|> Pipe.required "product" (Decode.map Product.Id Decode.string)
|> strictOptional "surfaceMass" Unit.decodeSurfaceMass
|> strictOptional "traceability" Decode.bool
|> DU.strictOptional "surfaceMass" Unit.decodeSurfaceMass
|> DU.strictOptional "traceability" Decode.bool
|> Pipe.optional "upcycled" Decode.bool False
|> strictOptional "yarnSize" Unit.decodeYarnSize


strictOptional : String -> Decoder a -> Decoder (Maybe a -> b) -> Decoder b
strictOptional field decoder =
-- Note: Using Json.Decode.Extra's optionalField here because we want
-- a failure when a Maybe decoded field value is invalid
DE.andMap (DE.optionalField field decoder)
|> DU.strictOptional "yarnSize" Unit.decodeYarnSize


decodeMaterialQuery : Decoder MaterialQuery
decodeMaterialQuery =
Decode.succeed MaterialQuery
|> strictOptional "country" Country.decodeCode
|> DU.strictOptional "country" Country.decodeCode
|> Pipe.required "id" (Decode.map Material.Id Decode.string)
|> Pipe.required "share" Split.decodeFloat
|> strictOptional "spinning" Spinning.decode
|> DU.strictOptional "spinning" Spinning.decode


encode : Query -> Encode.Value
Expand Down