Graphcool / graphcool-framework

Apache License 2.0
1.77k stars 131 forks source link

{ "error": "Function returned invalid status code: 0. Raw body: empty.last" } (facebook auth) #482

Open vendicto opened 6 years ago

vendicto commented 6 years ago

For bug reports, please fill in the next sections:

Current behavior

{ "error": "Function returned invalid status code: 0. Raw body: empty.last" }
Possible Unhandled Promise Rejection (id: 0):
Error: GraphQL error: A function returned an unhandled error. Please check the logs for executionId 'eu-west-1:simple:xxx'
Error: GraphQL error: A function returned an unhandled error. Please check the logs for executionId 'eu-west-1:simple:xxx'
    at new ApolloError (blob:http://localhost:8081/8c76b95f-35c2-4714-9300-393a22b880fc:59089:36)
    at Object.next (blob:http://localhost:8081/8c76b95f-35c2-4714-9300-393a22b880fc:59957:41)
    at SubscriptionObserver.next (blob:http://localhost:8081/8c76b95f-35c2-4714-9300-393a22b880fc:61624:18)
    at blob:http://localhost:8081/8c76b95f-35c2-4714-9300-393a22b880fc:64683:34
    at tryCallOne (blob:http://localhost:8081/8c76b95f-35c2-4714-9300-393a22b880fc:13415:14)
    at blob:http://localhost:8081/8c76b95f-35c2-4714-9300-393a22b880fc:13516:17
    at blob:http://localhost:8081/8c76b95f-35c2-4714-9300-393a22b880fc:14836:21
    at _callTimer (blob:http://localhost:8081/8c76b95f-35c2-4714-9300-393a22b880fc:14725:9)
    at _callImmediatesPass (blob:http://localhost:8081/8c76b95f-35c2-4714-9300-393a22b880fc:14761:9)
    at Object.callImmediates (blob:http://localhost:8081/8c76b95f-35c2-4714-9300-393a22b880fc:14980:14)

Reproduction

graphcool.yml

    type: resolver
    schema: src/facebook/facebookAuthentication.graphql
    handler:
      code: src/facebook/facebookAuthentication.ts

facebookAuthentication.graphql

type AuthenticateFacebookUserPayload {
  id: ID!
  token: String!
}

extend type Mutation {
  authenticateFacebookUser(facebookToken: String!): AuthenticateFacebookUserPayload!
}

facebookAuthentication.ts

import {fromEvent, FunctionEvent} from 'graphcool-lib'
import {GraphQLClient} from 'graphql-request'

interface User {
    id: string
}

interface FacebookUser {
    id: string
    email: string | null
}

interface EventData {
    facebookToken: string
}

export default async (event: FunctionEvent<EventData>) => {
    console.log(' 1 event ', event)

    try {
        const graphcool = fromEvent(event)
        const api = graphcool.api('simple/v1')

        const {facebookToken} = event.data

        // call Facebook API to obtain user data
        const facebookUser = await getFacebookUser(facebookToken)

        // get graphcool user by facebook id
        const user: User = await getGraphcoolUser(api, facebookUser.id)
            .then(r => r.User)

        // check if graphcool user exists, and create new one if not
        let userId: string | null = null

        if (!user) {
            // error happened on create user as I understand 
            userId = await createGraphcoolUser(api, facebookUser)
            return {data: {userId: userId }}
        } else {
            userId = user.id
        }

        // generate node token for User node
        const token = await graphcool.generateNodeToken(userId!, 'User')

        return {data: {id: userId, token}}
    } catch (e) {
        console.log(e)
        return {error: 'An unexpected error occured during authentication.'}
    }
}

async function getFacebookUser(facebookToken: string): Promise<FacebookUser> {
    console.log(' getFacebookUser ', facebookToken)
    const endpoint = `https://graph.facebook.com/v2.9/me?fields=id%2Cemail&access_token=${facebookToken}`
    const data = await fetch(endpoint)
        .then(response => response.json())

    if (data.error) {
        throw new Error(JSON.stringify(data.error))
    }

    return data
}

async function getGraphcoolUser(api: GraphQLClient, facebookUserId: string): Promise<{ User }> {
    console.log(' getGraphcoolUser ', api, facebookUserId)
    const query = `
    query getUser($facebookUserId: String!) {
      User(facebookUserId: $facebookUserId) {
        id
      }
    }
  `

    const variables = {
        facebookUserId,
    }

    return api.request<{ User }>(query, variables)
}

async function createGraphcoolUser(api: GraphQLClient, facebookUser: FacebookUser): Promise<string> {
    const mutation = `
    mutation createUser($facebookUserId: String!, $email: String) {
      createUser(
        facebookUserId: $facebookUserId
        email: $email
      ) {
        id
      }
    }
  `
  const variables = {
    facebookUserId: facebookUser.id,
    email: facebookUser.email
  }
  return api.request<{ createUser: User }>(mutation, variables)
    .then(r => r.createUser.id)
}

facebookCallback

 _facebookCallback = async facebookToken => {
      if (!!facebookToken) {
        console.log('facebookToken ', facebookToken)
// already have some token here, facebook API works fine 
        const graphcoolResponse = await this.props.authFbUser({variables: { facebookToken }})
//
        console.log('graphcoolResponse ', graphcoolResponse)
        const graphcoolToken = graphcoolResponse.data.authenticateUser.token
        AsyncStorage.setItem('SHORTLY_TOKEN', graphcoolToken)
        window.location.reload()
      } else {
        console.warn(`User did not authorize the Facebook application.`)
      }
    }

const authFbUser = graphql(AUTHENTICATE_FACEBOOK_USER, { name: 'authFbUser' });
const HomeScreen = compose(
    authFbUser,
    ...
);
export default HomeScreen(Home);

action

export const AUTHENTICATE_FACEBOOK_USER = gql`
    mutation AuthenticateUserMutation($facebookToken: String!) {
        authenticateFacebookUser(facebookToken: $facebookToken) {
            id
            token
        }
    }
`

schema

type User @model {
    id: ID! @isUnique
    fullName: String
    username: String @isUnique
    avatar: File @relation(name: "UserAvatar")
    dateOfBirth: DateTime
    email: String! @isUnique
    password: String
    age: Int
    createdAt: DateTime! # read-only (managed by Graphcool)
    updatedAt: DateTime! # read-only (managed by Graphcool)
    facebookUserId: String @isUnique
    posts: [Post!]! @relation(name: "UserPosts")
}

Expected behavior? return value will be

{
      id
      token
}
jesstelford commented 6 years ago

See The Workaround here: https://github.com/graphcool/graphcool-framework/issues/512

Kisepro commented 6 years ago

@jesstelford unfortunately the work around seems to not be related to the error message "Function returned invalid status code: 0. Raw body: empty.last".

I get this error message just adding some console.log and not because exception thrown.

jesstelford commented 6 years ago

That's a shame! Do you have an isolation case where you can see that by adding only a console.log(), it causes the error?

anaibol commented 6 years ago

Having also this issue with aws-sdk s3 upload.

pyankoff commented 6 years ago

Removing console.logs seems to help.

brodriguezs commented 5 years ago

yes, it's a shame!, I commented all my console.log calls and finally works. It's weird, because in my local works fine. Now I have to find another way to track the behavior of the backend logs 😭

grimunit commented 5 years ago

@brodriguezs one thing I have figured out as a workaround is using sentry and just forcing Raven.captureException(error, { extra: 'custom error message with ${variable}' } }). So just create a separate dev only sentry account for the sole purpose of debugging your graphcool functions more easily.

rubelux commented 5 years ago

@grimunit can you explain this a bit more?

grimunit commented 5 years ago

@rubelux basically use sentry as a replacement for console.log. So don't put the Raven.captureException in a catch block, just put it where you would put a console.log and send the variables along that you're trying to inspect. And they print it out really cleanly for you and it has just been a faster way to view and debug the state in my functions.