Проблемы интеграции графена Python с Федерацией Аполлона - PullRequest
1 голос
/ 03 июля 2019

Использование python для реализации GraphQL на нескольких микросервисах, некоторые используют Ariadne, а некоторые используют графен (и графен-Django).Из-за архитектуры микросервиса было выбрано, что Федерация Аполлона будет объединять схемы из разных микросервисов.

С Ariadne это очень просто (в первую очередь схема) и небольшой пример:

from ariadne import QueryType, gql, make_executable_schema, MutationType, ObjectType
from ariadne.asgi import GraphQL

query = QueryType()
mutation = MutationType()

sdl = """
type _Service {
    sdl: String
}

type Query {
    _service: _Service!
    hello: String
}
"""

@query.field("hello")
async def resolve_hello(_, info):
    return "Hello"


@query.field("_service")
def resolve__service(_, info):
    return {
        "sdl": sdl
    }

schema = make_executable_schema(gql(sdl), query)
app = GraphQL(schema, debug=True)

Теперь с Федерацией Аполлона это без проблем подобрано:

const { ApolloServer } = require("apollo-server");
const { ApolloGateway } = require("@apollo/gateway");


const gateway = new ApolloGateway({
    serviceList: [
      // { name: 'msone', url: 'http://192.168.2.222:9091' },
      { name: 'mstwo', url: 'http://192.168.2.222:9092/graphql/' },
    ]
  });

  (async () => {
    const { schema, executor } = await gateway.load();
    const server = new ApolloServer({ schema, executor });
    // server.listen();
    server.listen(
      3000, "0.0.0.0"
      ).then(({ url }) => {
      console.log(`? Server ready at ${url}`);
    });
  })();

, для которого я могу запускать запросы graphql к серверу на 3000.

Но, используя графен,Попытка реализовать ту же функциональность, что и у Ариадны:

import graphene

class _Service(graphene.ObjectType):
    sdl = graphene.String()

class Query(graphene.ObjectType):

    service = graphene.Field(_Service, name="_service")
    hello = graphene.String()

    def resolve_hello(self, info, **kwargs):
        return "Hello world!"

    def resolve_service(self, info, **kwargs):
        from config.settings.shared import get_loaded_sdl
        res = get_loaded_sdl()  # gets the schema defined later in this file
        return _Service(sdl=res)

schema = graphene.Schema(query=Query)

# urls.py
urlpatterns = [
    url(r'^graphql/$', GraphQLView.as_view(graphiql=True)),
]

, ... теперь приводит к ошибке федерации Apollo:

GraphQLSchemaValidationError: Тип Query должен определять одно или несколько полей.

Когда я проверил это, я обнаружил, что apollo вызывает микросервис с запросом graphql:

query GetServiceDefinition { _service { sdl } }

Запуск его на микросервисе через Insomnia / Postman / GraphiQL с Ariadne дает:

{
  "data": {
    "_service": {
      "sdl": "\n\ntype _Service {\n    sdl: String\n}\n\ntype Query {\n    _service: _Service!\n    hello: String\n}\n"
    }
  }
}

# Which expanding the `sdl` part:
type _Service {
    sdl: String
}

type Query {
    _service: _Service!
    hello: String
}

и на микросервисе с графеном:

{
  "data": {
    "_service": {
      "sdl": "schema {\n  query: Query\n}\n\ntype Query {\n  _service: _Service\n  hello: String\n}\n\ntype _Service {\n  sdl: String\n}\n"
    }
  }
}

# Which expanding the `sdl` part:
schema {
    query: Query
}

type Query {
    _service: _Service
    hello: String
}

type _Service {
    sdl: String
}

Итак, они оба одинаковы для определения, как получить sdl, проверяюоткликнулся на ответ микросервиса и обнаружил, что ответ графена также отправляет правильные данные, причем «данные» ответа Json равны:

execution_Result:  OrderedDict([('_service', OrderedDict([('sdl', 'schema {\n  query: Query\n}\n\ntype Query {\n  _service: _Service\n  hello: String\n}\n\ntype _Service {\n  sdl: String\n}\n')]))])

Так что может быть причиной того, что Федерация Аполлона не смоглауспешно получить эту микросервисную схему?

Ответы [ 2 ]

0 голосов
/ 03 июля 2019

Вы на правильном пути с другим ответом, но похоже, что вам нужно будет убрать некоторые вещи из печатной версии.

вот способ, который я использовал в выпуск github

Я суммирую свой код здесь:

schema = ""
class ServiceField(graphene.ObjectType):
    sdl = String()

    def resolve_sdl(parent, _):
        string_schema = str(schema)
        string_schema = string_schema.replace("\n", " ")
        string_schema = string_schema.replace("type Query", "extend type Query")
        string_schema = string_schema.replace("schema {   query: Query   mutation: MutationQuery }", "")
        return string_schema


class Service:
    _service = graphene.Field(ServiceField, name="_service", resolver=lambda x, _: {})

class Query(
    # ...
    Service,
    graphene.ObjectType,
):
    pass

schema = graphene.Schema(query=Query, types=CUSTOM_ATTRIBUTES_TYPES)
0 голосов
/ 03 июля 2019

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

Так что, если в Ариадне, я добавлю

schema {
    query: Query
}

в sdl, Федерация Аполлона также повышает Type Query must define one or more fields.. Без этого все работает нормально. Тогда я также пошел к графену и в функции resolve_service сделал:

def resolve_service(self, info, **kwargs):
    from config.settings.shared import get_loaded_sdl
    res = get_loaded_sdl()
    res = res.replace("schema {\n  query: Query\n}\n\n", "")
    return _Service(sdl=res)

И теперь графен тоже работает, так что, думаю, проблема была в том, что я упустил из виду, похоже, что Федерация Аполлона не может обработать грамматику схемы:

schema {
    query: Query
}

Обновление 1

Строка, которую я не заметил на сайте Аполлона, такова:

Этот SDL не включает дополнения спецификации федерации выше. С учетом ввода, как это:

Это понятно при объединении служб в федерации, поскольку это вызовет ошибку:

GraphQLSchemaValidationError: Field "_Service.sdl" can only be defined once.

Итак, хотя в полной схеме для микросервиса с определением _Service.sdl мы хотим, чтобы эта информация ушла для строки полной схемы, которая возвращается в качестве возвращаемой строки для _Service.sdl

Обновление 2

Федерация Apollo теперь работает нормально, убедившись, что строка, возвращаемая полем sdl, не содержит спецификации федерации.

В графене я думаю, что каждая реализация может отличаться, но в целом вы хотите заменить следующее:

res = get_loaded_sdl()
res = res.replace("schema {\n  query: Query\n}\n\n", "")
res = res.replace("type _Service {\n  sdl: String\n}", "")
res = res.replace("\n  _service: _Service", "")

А в Ариадне просто необходимо определить два sdl, один из которых содержит спецификации федерации (для схемы, возвращаемой службой), а другой - без спецификации федерации (тот, который возвращается в поле sdl)

...