简体   繁体   English

使用函数生成器将值返回到GraphQL Mutation中的outputFields

[英]Return value to outputFields in GraphQL Mutation using a Function Generator

I am having problems using a function generator inside a GraphQL mutation . 我在GraphQL突变内部使用函数生成器时遇到问题。

The function generator is being used to clean up the promise chain inside the mutation. 函数生成器用于清理突变内部的promise链。 Before refactoring the code was working fine, and at present the console.logs are showing the newly created writes to the database so as far as I can tell the issue is getting the final return value out of the function generator and setting it as the return value of the mutateAndGetPayload function. 在重构代码之前,代码可以正常工作,目前console.logs正在显示新创建的对数据库的写入,据我所知,问题是从函数生成器中获取了最终返回值并将其设置为return mutateAndGetPayload函数的值。

Error 错误

Cannot set property 'clientMutationId' of undefined 无法设置未定义的属性“ clientMutationId”

I have been at this for a while now and would appreciate any advice or help. 我已经有一段时间了,希望能给您任何建议或帮助。

How do I get the return value of the function generator into the scope of the enclosing function so I can return it from that function? 如何将函数生成器的返回值放入封闭函数的范围内,以便可以从该函数返回它?


Below is the mutation. 下面是突变。 The database writes work fine, and the output works when it gets the right payload (it is not working, which is my main issue). 数据库写工作正常,并且输出在获得正确的有效负载时起作用(它不起作用,这是我的主要问题)。 The only issue is getting the newUser to the resolve function in the output field. 唯一的问题是让newUser进入输出字段中的resolve函数。 I have omitted some of the code to simplify the issue. 我省略了一些代码以简化问题。

const addEmployeeMutation = mutationWithClientMutationId({
  name: 'AddEmployee',
  inputFields: {
    /** this section works fine **/
  },
  outputFields: {
    attendee: {
      type: AttendeeType,
      resolve: r.compose(
          /** this section works fine **/
          r.prop('id')
        )
    }
  },
  mutateAndGetPayload: (payload, {userId}) => {
    const getUser = (userId) => {
      return authorize(userId)
        .then(can => /** works fine **/ )
        .then(user => gen.next(user))
    }    
    const createHash = (plainPassword) => {
      return genSalt(10)
        .then((salt, err) => { /** works fine **/ })
        .then((hashed_password, err) => gen.next(hashed_password))
    }   
    const createUser = (payload, hashed_password, user) => {
      return new UserModel( /** works fine **/ ).save()
        .then(newUser => gen.next(newUser))
    }    
    const createProfile = (payload, newUserId) => {
      return new ProfileModel({ /** works fine **/ })
        .then(profile => gen.next(profile))
        .catch(err => {throw new GraphQLError(`Error: ${err}`)})
    }  
    let gen = generator()
    gen.next()    
    function* generator() {   
      const user = yield getUser(userId)
      console.log('USER >>>>>>>>>>>>>>>>>>', user)
      const hash = yield createHash(payload.password)
      console.log('HASH >>>>>>>>>>>>>>>>>>', hash)   
      const newUser = yield createUser(payload, hash, user)
      console.log('NEW USER >>>>>>>>>>>>>>>>>> ', newUser)    
      const newProfile = yield createProfile(payload, newUser.id)
      console.log('NEW PROFILE >>>>>>>>>>>>>>>>>> ', newProfile)    
      return newUser
    }
    return newUser // <- How do I get a value to here?
}

Async Await 异步等待

Thanks to some help from @Bergi I changed to using async / await . 感谢@Bergi的帮助,我改为使用async / await

I am posting my answer here in case someone else goes down this road and needs a hand. 如果有人走这条路并需要帮助,我会在这里发布我的答案。

The answer turns out to be a quick fix. 答案很快就解决了。

Change the .then() in each of the promises that are doing the database writes to return the needed object. 在执行数据库写操作的每个promise中更改.then() ,以返回所需的对象。 And then moved them into a separate utility module. 然后将它们移到单独的实用程序模块中。

In the mutation itself I use the following code to run through the async writes and return the required payload to the output field: 在变异本身中,我使用以下代码运行异步写入,并将所需的有效负载返回到输出字段:

mutateAndGetPayload: (payload, {userId}) => {
  async function generateUser() {
    const user = await getUser(userId)
    const hash = await createHash(payload.password)
    const newUser = await createUser(payload, hash, user)
    await createProfile(payload, newUser.id)
    return newUser
  }
  return generateUser()
}

edit 编辑

An even neater solution, thanks again to @Bergi. 甚至更整洁的解决方案,再次感谢@Bergi。 I am leaving the original solution as it provides some clarity as to how async / await is being used, and demonstrates how it is possible to refactor code to improve terseness. 我将离开原始解决方案,因为它提供了有关如何使用async / await一些清晰信息,并演示了如何重构代码以提高简洁性。 Again, someone may find this useful. 同样,有人可能会发现这很有用。

mutateAndGetPayload: async function(payload, {userId}) {
  const user = await getUser(userId)
  const hash = await createHash(payload.password)
  const newUser = await createUser(payload, hash, user)
  await createProfile(payload, newUser.id)
  return newUser
}

Explanation 说明

As the use of a function generator in the OP was asynchronous and hence non-blocking, the mutateAndGetPayload function was returning undefined before the generator had time to complete. 由于在OP中使用函数生成器是异步的,因此是非阻塞的,因此mutateAndGetPayload函数在生成器有时间完成之前返回undefined By using async / await (in both versions) the mutateAndGetPayload function was blocked from completing until add the await values had resolved in the order in which they were specified. 通过使用async / await (在两个版本中), mutateAndGetPayload函数被阻止完成,直到添加await值按指定的顺序解析为止。

Ramda JS 拉姆达·JS

Another approach that makes use of the ramda functional programming library. 利用ramda功能编程库的另一种方法。 With some help from q to manage the promises. q帮助下管理承诺。

Using ramda.js involves currying the utility functions so that they are waiting for the data to come in the right order. 使用ramda.js涉及到遍历实用程序函数,以便它们等待数据以正确的顺序出现。

mutateAndGetPayload: (payload, {userId}) =>
  r.compose(
    then(
      r.compose(
        then(r.last),
        r.converge(
          r.unapply(q.all),
          [
            createProfile(payload),
            createUserRole(payload),
            r.identity
          ]
        )
      )
    ),
    r.converge(
      r.compose(
        then(createUser(payload)),
        r.unapply(q.all)
      ),
      [
        r.identity,
        then(_ => createHash(payload))
      ]
    ),
    getUser
  )(userId)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM