这个解析器扩展功能是如何工作的?

How does this resolver extension function work?

我需要帮助解释一段代码。

我一直在尝试学习如何使用 jwt 以及返回令牌和刷新令牌对用户进行身份验证。

这是一次有趣的旅程,我遇到了这个 repository,其中用户添加了一个 extension/chain 函数,据我所知,它结合了多个 graphql 解析器:

// I don't understand this function
const createResolver = (resolver) => {
  const baseResolver = resolver;
  baseResolver.createResolver = (childResolver) => {
    const newResolver = async (parent, args, context, info) => {
      await resolver(parent, args, context, info);
      return childResolver(parent, args, context, info);
    };
    return createResolver(newResolver);
  };
  return baseResolver;
};

export const requiresAuth = createResolver((parent, args, context) => {
  if (!context.user || !context.user.id) {
    throw new Error('Not authenticated');
  }
});

export const requiresAdmin = requiresAuth.createResolver((parent, args, context) => {
  if (!context.user.isAdmin) {
    throw new Error('Requires admin access');
  }
});

它是这样使用的:

Query: { 
    getBook: requiresAuth.createResolver((parent, args, { models }, info) =>
       // do something fun
    ),
}

我不明白 createResolver 函数,我想问的是这是否是某种编程范例,其中有我可以阅读和更好理解的文章。

谷歌搜索我确实找到了 graphql-resolvers,据我所知,它做同样的事情,但在这种情况下,我需要安装另一个我想避免的包,但与此同时,我不想使用我不完全理解的功能。

编辑:

我所了解的 function/think 我所了解的:

通过调用一个函数然后在该函数内部传入一个新函数来将多个函数链接在一起:requiresAuth.createResolver 在函数本身内部,我无法弄清楚。 我们得到一个新变量 baseResolver 我们链接一个新的 createResolver 然后我们递归地调用父函数。

我猜我无法理解这个流程。

我冒昧地重命名了一些东西,因为现有的名字很混乱。

const augment = (resolver) => {
  resolver.add = (nextResolver) => {
    const wrapper = async (parent, args, context, info) => {
      await resolver(parent, args, context, info)
      return nextResolver(parent, args, context, info)
    }
    return augment(wrapper)
  }
  return resolver
}

在方法的每一步都将 .add 方法添加到 returned 函数,以启用链接,如下所示:

augment(resolver1).add(resolver2).add(resolver3)...

每次调用 .add 时,都会创建一个新的异步 lambda 函数 (wrapper)。 wrapper 收盘超过 resolvernextResolver。当最终是运行时,wrapper会调用resolver,等待结果,然后调用nextResolver,return得到结果。

]

wrapper 然后传递给 augment 以向其添加 .add 函数(以启用链接)。这个新的 add 函数关闭提供给 augment 的参数(即 wrapper)。增强的 wrapper 函数是 returned.

因此,当随后调用 .add 时,resolver 参数是在前一个解析器上调用 .addwrapper 函数。所以:

await resolver(parent, args, context, info)
return nextResolver(parent, args, context, info)

...将等待先前创建的 wrapper 函数,然后调用最新的 nextResolver.

通过这种方式,对 .add 的连续调用构建了一个异步函数链。

当你想要运行链时,你可以简单地调用returned函数,忽略上面的.add 属性。

请注意,每个解析器都会传递相同的参数,以避免用户担心将它们传递到链中。

函数可以重写为:

const augment = (resolver) => {
  resolver.add = (nextResolver) => 
    augment((...args) => 
      resolver(...args)
        .then(() => nextResolver(...args)))
  return resolver
}

...或者:

const inSequence = (resolvers) => 
    (...args) => 
        resolvers.reduce((acc, el) => 
            acc.then(() => el(...args)), Promise.resolve())

const getBook = inSequence([auth, admin, getBookResolver])
const query = { getBook }

...或者:

const inSequence = (resolvers) => 
    async (...args) => {
        for(let el of resolvers) {
            await el(...args)
        }
    }
const getBook = inSequence([auth, admin, getBookResolver])
const query = { getBook }
const createResolver = (resolver) => {
  const baseResolver = resolver;
  baseResolver.createResolver = (childResolver) => {
    const newResolver = async (parent, args, context, info) => {
      await resolver(parent, args, context, info);
      return childResolver(parent, args, context, info);
    };
    return createResolver(newResolver);
  };
  return baseResolver;
};

export const requiresAuth = createResolver( /*resolver_A*/ (parent, args, context) => {
  if (!context.user || !context.user.id) {
    throw new Error('Not authenticated');
  }
});

requiresAuth returned by createResolver 是一个函数(作为参数传递,用 resolver_A 标记)装饰有内部附加函数 ... available by .createResolver()

export const requiresAdmin = requiresAuth.createResolver( /*resolver_B*/ (parent, args, context) => {
  if (!context.user.isAdmin) {
    throw new Error('Requires admin access');
  }
});

requiresAuth.createResolver( 使用作为参数传递的另一个解析器调用(resolver_BchildResolver)创建并 return 新的异步解析器 newResolvernewResolver 当调用 awaits 以获得 resolver 的结果(在本例中为 resolver_A),然后调用当前传递的解析器(resolver_B)。

createResolver(newResolver); 不仅 return 链式异步解析器 - 函数再次 ('reccurency') 装饰有 .createResolver 允许链式解析器。您可以再次使用 .createResolver():

Query: { 
  editBook: requiresAdmin.createResolver((parent, args, { models }, info) =>

editBook 将在 运行 目标解析器之前依次检查身份验证 (if (!context.user || !context.user.id) ...) 和权限 (if (!context.user.isAdmin) ...)。

等于

editBook: createResolver(authResolverFn).createResolver(adminResolverFn).createResolver(editBookResolverFn)

...但只有第一个 createResolver 是 'external' 函数的名称。