dillonkearns / elm-graphql

Autogenerate type-safe GraphQL queries in Elm.
https://package.elm-lang.org/packages/dillonkearns/elm-graphql/latest
BSD 3-Clause "New" or "Revised" License
779 stars 107 forks source link

Failure "Expecting an OBJECT with a field named `[fieldname]1675858143`" #252

Closed datakurre closed 4 years ago

datakurre commented 4 years ago

I must be doing something horribly wrong, because I'm quite new to Elm, trying to do GraphQL with it for the first time, and could not find anyone having similar issue. I wonder, if I have an obvious mistake someone could point to me:

I'm unable to decode value send through port unless I rename the keys in the value to match the keys I see being expected in error. For example, when I get error like:

error:: Field "data" (Field "study_space" (Index 0 (Field "sensors" (Index 0 (Field "vibrations" (Index 0 (Failure "Expecting an OBJECT with a field named `voltage1675858143`" <internals>)))))))

The error goes away and JSON is decoded into Elm values proplerly after I have renamed key "voltage" into "voltage1675858143" in my data (and similar error for every other key in my data).

My example code looks like:

port module Main exposing (init)

import Browser
import Graphql.Document
import Graphql.Operation exposing (RootQuery, RootSubscription)
import Graphql.SelectionSet as SelectionSet exposing (SelectionSet, with)
import Html exposing (..)
import Json.Decode
import SensorApi.Object
import SensorApi.Object.Sigfox_sensor as Sigfox_sensor
import SensorApi.Object.Sigfox_vibration as Sigfox_vibration
import SensorApi.Object.Study_space as Study_space
import SensorApi.ScalarCodecs exposing (Float8, Timestamptz)
import SensorApi.Subscription as Subscription exposing (SigfoxSensorOptionalArguments, SigfoxVibrationOptionalArguments, StudySpaceOptionalArguments)
import String

type alias StudySpace =
    { id : String
    , sensors : List Sensor
    }

type alias Sensor =
    { id : String
    , vibrations : List Vibration
    }

type alias Vibration =
    { seqNumber : Maybe Int
    , temperature : Maybe Int
    , timestamp : Maybe Timestamptz
    , voltage : Maybe Float8
    }

vibrationSelection : SelectionSet Vibration SensorApi.Object.Sigfox_vibration
vibrationSelection =
    SelectionSet.succeed Vibration
        |> with Sigfox_vibration.seqNumber
        |> with Sigfox_vibration.temperature
        |> with Sigfox_vibration.timestamp
        |> with Sigfox_vibration.voltage

sensorSelection : SelectionSet Sensor SensorApi.Object.Sigfox_sensor
sensorSelection =
    SelectionSet.succeed Sensor
        |> with Sigfox_sensor.id
        |> with (Sigfox_sensor.vibrations (\x -> x) vibrationSelection)

studySpaceSelection : SelectionSet StudySpace SensorApi.Object.Study_space
studySpaceSelection =
    SelectionSet.succeed StudySpace
        |> with Study_space.id
        |> with (Study_space.sensors (\x -> x) sensorSelection)

subscriptionDocument : SelectionSet (List StudySpace) RootSubscription
subscriptionDocument =
    Subscription.study_space (\x -> x) studySpaceSelection

main : Program () Model Msg
main =
    Browser.element
        { init = \() -> init
        , view = view
        , update = update
        , subscriptions = subscriptions
        }

-- SUBSCRIPTIONS

port gotStudySpaceData : (Json.Decode.Value -> msg) -> Sub msg

subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.batch
        [ gotStudySpaceData StudySpaceDataReceived
        ]

-- MODEL

type alias Model =
    { study_spaces : List StudySpace }

-- VIEW

view : Model -> Html Msg
view model =
    text (String.fromInt (List.length model.study_spaces))

-- UPDATE

type Msg
    = StudySpaceDataReceived Json.Decode.Value

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        StudySpaceDataReceived newData ->
            case Json.Decode.decodeValue (subscriptionDocument |> Graphql.Document.decoder) newData of
                Ok studySpaceData ->
                    ( { model | study_spaces = studySpaceData }, Cmd.none )

                Err error ->
                    let
                        _ =
                            Debug.log "error:" error
                    in
                    ( model, Cmd.none )

-- INIT

init : ( Model, Cmd Msg )
init =
    ( Model [], Cmd.none )

Versions

    "dependencies": {
        "direct": { 
            "dillonkearns/elm-graphql": "4.4.0",
            "elm/browser": "1.0.2",
            "elm/core": "1.0.2",
            "elm/html": "1.0.0",
            "elm/json": "1.1.3",
            "elm/time": "1.0.0"
        },
        "indirect": { 
            "Skinney/murmur3": "2.0.8",
            "elm/bytes": "1.0.8",
            "elm/file": "1.0.5",
            "elm/http": "2.0.0",
            "elm/regex": "1.0.0",
            "elm/url": "1.0.0",
            "elm/virtual-dom": "1.0.2",
            "elm-community/list-extra": "8.2.2",
            "lukewestby/elm-string-interpolate": "1.0.4"
        } 
    },
Jayshua commented 4 years ago

When you say data are you referring to the data coming from the server?

elm-graphql generates queries with a random hash added to each field like this:

query {
  studyspace1235123: studySpace {
    id2415235: id
  }
}

so that you can query the same endpoint multiple times in a single query. It kind of sounds like your graphql server isn't returning a response with the requested key names.

datakurre commented 4 years ago

@Jayshua That explains everything. My issue was that I was not using elm-graphql to generate my queries, but was trying to just use it to decode data queried outside Elm (because WebSocket subscriptions cannot be done in Elm anyway yet).

Once I used elm-graphql to also build the query, everything worked.

Thanks a lot. This really helped to be back on track.