Как работает эта функция расширения решателя? - PullRequest
2 голосов
/ 31 марта 2020

Мне нужна помощь в интерпретации кода.

Я пытался узнать, как аутентифицировать пользователей с помощью jwt, возвращать токены и обновлять sh токены.

Это была забавная поездка, и я наткнулся на этот репозиторий , где пользователь добавил функцию расширения / цепочки, которая, насколько я понимаю, объединяет несколько распознавателей 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, и я спрашиваю, является ли это какой-то парадигмой программирования, где есть статьи, которые я могу прочитать и лучше понять ,

Поиск в Google Я нашел graphql-resolvers, который, насколько я понимаю, делает то же самое, но в этом случае мне нужно будет установить еще один пакет, которого я хотел бы избежать, но в то же время я не не хочу использовать функцию, которую я не до конца понимаю.

Редактировать:

Что я понимаю о функции / думаю, что понимаю:

объединяет несколько функций вместе, вызывая одну функцию, а затем внутри этой функции передавая новую функцию : requiresAuth.createResolver Внутри самой функции у меня проблемы с ее выяснением. Мы получаем новую переменную baseResolver, в которую мы добавляем новую цепочку createResolver, где мы затем рекурсивно вызываем родительскую функцию.

Я не могу понять поток этого, я думаю.

Ответы [ 2 ]

2 голосов
/ 31 марта 2020

Я позволил себе переименовать некоторые вещи, потому что существующие имена очень запутаны.

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, чтобы включить цепочку следующим образом:

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

Каждый раз, когда вызывается .add, создается новая асинхронная c лямбда-функция (wrapper). wrapper закрывается как resolver, так и nextResolver. Когда он в конце концов будет запущен, wrapper вызовет resolver, дождется результата, а затем вызовет nextResolver и вернет результат.

wrapper затем передается в augment для добавления .add функция к нему (для включения цепочки). Эта новая функция add закрывает аргумент, переданный augment (то есть wrapper). Возвращается расширенная функция wrapper.

Итак, когда впоследствии вызывается .add, аргумент resolver является функцией wrapper вызова .add в предыдущем преобразователе. Итак:

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

... будет ожидать ранее созданную функцию wrapper, а затем вызовет самую последнюю nextResolver.

Таким образом, последовательные вызовы конструкции .add цепочка асин c функций.

И когда вы хотите запустить цепочку, вы можете просто вызвать возвращенную функцию, игнорируя свойство .add для нее.

Обратите внимание, что каждый resolver передается те же аргументы, чтобы пользователю не приходилось беспокоиться о передаче их по цепочке.

Функция может быть переписана как:

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 }
1 голос
/ 31 марта 2020
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, возвращаемое createResolver - это функция (переданная как параметр, отмеченная resolver_A), украшенная дополнительной функцией внутри ... доступной .createResolver()

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

requiresAuth.createResolver( вызывается с другим преобразователем, переданным как параметр (resolver_B is childResolver) создает и возвращает новый asyn c resolver newResolver. newResolver при вызове await s для результатов resolver (в данном случае resolver_A), а затем вызовы текущего принятого преобразователя (resolver_B).

createResolver(newResolver); не только возвращает цепочку asyn c resolver - функция снова ('recuurency') украшена .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 - это название «внешней» функции.

...