温馨提示:本文翻译自stackoverflow.com,查看原文请点击:graphql - Problems integrating Python graphene with Apollo Federation
graphql python-3.x microservices apollo apollo-server

graphql - 将Python石墨烯与Apollo Federation集成时出现问题

发布于 2020-03-27 11:44:02

使用python在多个微服务之间实现GraphQL,有些使用Ariadne,有些使用graphene(和graphene-Django)。由于微服务架构的原因,选择了Apollo Federation将合并来自不同微服务的架构。

使用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)

现在,使用Apollo Federation可以毫无问题地进行选择:

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

但是,通过使用石墨烯,尝试实现与Ariadne相同的功能:

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)),
]

,...现在导致来自阿波罗联盟的错误:

GraphQLSchemaValidationError: Type Query must define one or more fields.

As I checked into this matter, I found that apollo calls the microservice with a graphql query of:

query GetServiceDefinition { _service { sdl } }

Running it on the microservice via Insomnia/Postman/GraphiQL with Ariadne gives:

{
  "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
}

and on the microservice with Graphene:

{
  "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
}

So, they both are the same thing for defining how to get sdl, I checked into the microservice response, and found that graphene response is sending the correct data too, with the Json response "data" being equal to:

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')]))])

So what could the reason be for Apollo Federation not being able to successfully get this microservice schema?

查看更多

查看更多

提问者
jupiar
被浏览
146
5,153 2019-08-15 00:04

解决方案实际上是稍微修改,该架构是通过自动生成的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)

现在石墨烯也可以使用,所以我想这个问题被我忽略了,看来Apollo Federation无法处理以下形式的语法:

schema {
    query: Query
}

更新1

我没有在Apollo网站上注意到的一句话是:

该SDL不包括上述联邦规范的增加。给定这样的输入:

在联合中将服务组合在一起时,这很明显,因为它将引发错误:

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

因此,尽管在具有define的微服务的完整架构中_Service.sdl,我们希望该信息消失在完整架构的字符串中,该字符串作为以下项的返回字符串返回:_Service.sdl

更新2

阿波罗联盟现在可以正常工作,并确保该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!", "")

在Ariadne中,只需定义两个sdl,一个包含联合规范(用于服务返回的架构),另一个不包含联合规范(由sdl字段返回)。