module Graphql.Field exposing ( map , mapOrFail, nonNullOrFail, nonNullElementsOrFail , Field(..) ) {-| `Field`s are automatically generated by the `@dillonkearns/elm-graphql` CLI command. You can use `Graphql.Field.map` to transform a value. ## Safe Transformations @docs map ## Result (`...OrFail`) Transformations **Warning** When you use these functions, you lose the guarantee that the server response will decode successfully. These helpers, though convenient, will cause your entire decoder to fail if it ever maps to an `Err` instead of an `Ok` `Result`. @docs mapOrFail, nonNullOrFail, nonNullElementsOrFail ## Types @docs Field -} import Graphql.RawField as Field exposing (RawField) import Json.Decode as Decode exposing (Decoder) {-| -} type Field decodesTo typeLock = Field RawField (Decoder decodesTo) {-| Maps the data coming back from the GraphQL endpoint. In this example, `User.name` is a function that the `@dillonkearns/elm-graphql` CLI tool created which tells us that the `name` field on a `User` object is a String according to your GraphQL schema. import Api.Object import Api.Object.User as User import Graphql.Field as Field import Graphql.SelectionSet exposing (SelectionSet, with) human : SelectionSet String Api.Object.User human = User.selection identity |> with (User.name |> Field.map String.toUpper ) You can also map to values of a different type (`String -> Int`, for example), see [`examples/StarWars.elm`](https://github.com/dillonkearns/elm-graphql/blob/master/examples/src/Starwars.elm) for more advanced example. -} map : (decodesTo -> mapsTo) -> Field decodesTo typeLock -> Field mapsTo typeLock map mapFunction (Field field decoder) = Field field (Decode.map mapFunction decoder) {-| If the map function provided returns an `Ok` `Result`, it will map to that value. If it returns an `Err`, the _entire_ response will fail to decode. import Date exposing (Date) import Github.Object import Github.Object.Repository import Github.Scalar import Graphql.Field as Field exposing (Field) createdAt : Field Date Github.Object.Repository createdAt = Github.Object.Repository.createdAt |> Field.mapOrFail (\(Github.Scalar.DateTime dateTime) -> Date.fromString dateTime ) -} mapOrFail : (decodesTo -> Result String mapsTo) -> Field decodesTo typeLock -> Field mapsTo typeLock mapOrFail mapFunction (Field field decoder) = decoder |> Decode.map mapFunction |> Decode.andThen (\result -> case result of Ok value -> Decode.succeed value Err errorMessage -> Decode.fail ("Check your code for calls to mapOrFail, your map function returned an `Err` with the message: " ++ errorMessage) ) |> Field field {-| Effectively turns an attribute that is `String` => `String!`, or `User` => `User!` (if you're not familiar with the GraphQL type language notation, learn more [here](http://graphql.org/learn/schema/#type-language)). This will cause your _entire_ decoder to fail if the field comes back as null. It's far better to fix your schema then to use this escape hatch! -} nonNullOrFail : Field (Maybe decodesTo) typeLock -> Field decodesTo typeLock nonNullOrFail (Field field decoder) = decoder |> Decode.andThen (\result -> case result of Just value -> Decode.succeed value Nothing -> Decode.fail "Expected non-null but got null, check for calls to nonNullOrFail in your code. Ideally your schema should indicate that this is non-nullable so you don't need to use nonNullOrFail at all." ) |> Field field {-| Effectively turns a field that is `[String]` => `[String!]`, or `[User]` => `[User!]` (if you're not familiar with the GraphQL type language notation, learn more [here](http://graphql.org/learn/schema/#type-language)). This will cause your _entire_ decoder to fail if any elements in the list for this field comes back as null. It's far better to fix your schema then to use this escape hatch! Often GraphQL schemas will contain things like `[String]` (i.e. a nullable list of nullable strings) when they really mean `[String!]!` (a non-nullable list of non-nullable strings). You can chain together these nullable helpers if for some reason you can't go in and fix this in the schema, for example: releases : SelectionSet (List Release) Github.Object.ReleaseConnection releases = Github.Object.ReleaseConnection.selection identity |> with (Github.Object.ReleaseConnection.nodes release |> Field.nonNullOrFail |> Field.nonNullElementsOrFail ) Without the `Field.nonNull...` transformations here, the type would be `SelectionSet (Maybe (List (Maybe Release))) Github.Object.ReleaseConnection`. -} nonNullElementsOrFail : Field (List (Maybe decodesTo)) typeLock -> Field (List decodesTo) typeLock nonNullElementsOrFail (Field field decoder) = decoder |> Decode.andThen (\result -> case combineMaybeList result of Nothing -> Decode.fail "Expected only non-null list elements but found a null. Check for calls to nonNullElementsOrFail in your code. Ideally your schema should indicate that this is non-nullable so you don't need to use nonNullElementsOrFail at all." Just listWithoutNulls -> Decode.succeed listWithoutNulls ) |> Field field combineMaybeList : List (Maybe a) -> Maybe (List a) combineMaybeList listOfMaybes = let step maybeElement accumulator = case maybeElement of Nothing -> Nothing Just element -> Maybe.map ((::) element) accumulator in List.foldr step (Just []) listOfMaybes