GraphQL从零开始的应用实践-OSGI框架

实现效果展示

组件对外提供了一个Rest用于传递输入的GQL语句与入参,如图所示,展示了一个按协议类型查询端口id和名称的例子:
在这里插入图片描述
查询语句:

{
  "query": "query{filterLtp(key:\"layer\",value:[\"IP\"]){ltp{id,name}}}",
  "variables": {}
}

返回结果如图:
在这里插入图片描述

schema描述

模型定义

定义需要返回展示的数据模型,由于gql的schema模型不直接支持map结构,所以建议在设计模型时避免使用map结构。否则需要借助gql的列表和union来组装实现,多了一层模型的封装。以下为返回的Link模型部分定义:

type GraphLink {
    link: LinkEx
    srcLtp: LtpEx
    dstLtp: LtpEx
}
type LinkEx {
    id: String
    from: From
    name: String
    layer: Layer
    controlDomain: String
    manageDomain: String
    key: String
    modelType: ModelType
    i18nLabel: I18nLabel
    moc: String
    displayName: String
    fromId: String
    srcTopoId: String
    srcNodeId: String
    srcTpId: String
    dstTopoId: String
    dstNodeId: String
    dstTpId: String
    direction: Direction
    state: State
    faultAlert: FaultAlert
    grFlag: Boolean
    isMultiDomain: Boolean
    linkExtensions: [LinkExtension]
}
union LinkExtension = IpLinkExtension | EthLinkExtension
type IpLinkExtension {
    igpMetric: Int
    performance: Performance
    capacity: Capacity
    routeDomain: String
    srlg: [Int]
    adjSid: String
    peerNodeSid: String
    peerAdjSid: String
    linkMaxStackDepth: String
    affinity: Int
    linkMtId: Int
}
type Performance {
    id: String
    modelType: ModelType
    metric: Int
    delay: Int
    jitter: Int
    wander: Int
    queueDelay: String
    averageRate: Float
    originMetric: Int
}

针对Link模型封装了一层LinkEx,其主要的原则即是标准的Link模型中使用了map结构

    @ApiModelProperty(value = "扩展属性")
    private Map<String, LinkExtension> extensions = new ConcurrentHashMap<String, LinkExtension>();

GQL中不支持对于extension直接描述,所以封装了一层,使用 linkExtensions: [LinkExtension]作了适配,更进一步,如果extensions定义成List<LinkExtension>则可以直接使用Link模型。而目录不得已需要使用LinkEx模型,如下:

public class LinkEx extends Link implements Res{
    private List<LinkExtension> linkExtensions;
}

接口定义

schema {
    query: graphTopoQuery
}
type graphTopoQuery {
    filterLink(key:String,value:[String],offset:Int,limit:Int) : [GraphLink]
    }

定义一个4个入参的返回为GraphLink列表的接口。

开发实现

POM依赖

        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-java</artifactId>
            <version>8.0</version>
        </dependency>
        <dependency>
            <groupId>org.reactivestreams</groupId>
            <artifactId>reactive-streams</artifactId>
            <version>1.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.antlr</groupId>
            <artifactId>antlr4-runtime</artifactId>
            <version>4.7.2</version>
        </dependency>

scheam加载

TypeDefinitionRegistry typeRegistry = TopologyTypeDefinitionRegistry.load("graphLink.graphqls");
    public static TypeDefinitionRegistry load(String gql) {
        InputStream inputStream = TopologyTypeDefinitionRegistry.class.getClassLoader().getResourceAsStream(gql);
        SchemaParser schemaParser = new SchemaParser();
        try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream)) {
            return schemaParser.parse(inputStreamReader);
        } catch (IOException e) {
            logger.error("TopologyTypeDefinitionRegistry error.", e);
        }
        return null;
    }

数据Fetcher绑定

 RuntimeWiring wiring = TopologyRuntimeWiring.builder()
 .type("Res", typeWiring -> typeWiring.typeResolver(getTypeResolverOfRes()))
                .type("graphTopoQuery", typeWiring -> typeWiring
                        .dataFetcher("filterLink", graphLinkFilterDataFetcher).type("LinkEx", typeWiring -> typeWiring.dataFetcher("linkExtensions", graphIpLinkExtensionFilterDataFetcher)).type("GraphLink", typeWiring -> typeWiring.dataFetcher("srcLtp", srcLtpDataFetcher).dataFetcher("srcLtp", dstLtpDataFetcher))
                          .build();

    public static TypeResolver getTypeResolverOfRes() {
        return new TypeResolver() {
            @Override
            public GraphQLObjectType getType(TypeResolutionEnvironment env) {
                Object javaObject = env.getObject();
                if (javaObject instanceof NodeEx) {
                    return env.getSchema().getObjectType("NodeEx");
                } else if (javaObject instanceof LinkEx) {
                    return env.getSchema().getObjectType("LinkEx");
                } 
            }
        };
    }

针对需要二次查询的都需要定义DataFetecher

完成GraphQL创建

        SchemaGenerator schemaGenerator = new SchemaGenerator();
        GraphQLSchema graphQLSchema = schemaGenerator.makeExecutableSchema(typeRegistry, wiring);
        graphQL = GraphQL.newGraphQL(graphQLSchema).build();

对外接口:

    public String query(GraphInput input) {
        long currentTime = System.currentTimeMillis();
        ExecutionInput executionInput = ExecutionInput.newExecutionInput().variables(input.getVariables())
                .query(input.getQuery())
                .build();
        Map<String, Object> result = graphQL.execute(executionInput).toSpecification();
        logger.info("Rest Invoke graphql query with input:[{}] cost:{} ms", input, System.currentTimeMillis() - currentTime);
        return GraphResult.serialize(result);
    }

入参定义:

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
@ApiModel(description = "com.zte.sdn.oscp.topology.model.graph.input.GraphInput")
public class GraphInput {
    private String query;
    private Map<String, Object> variables;
}

接口定义:

    @POST
    @Path("graphql")
    @ApiOperation(value = "基于Graph查询接口")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "OK", response = String.class),
            @ApiResponse(code = 400, message = "operation got an error")
    })
    String query(
            GraphInput input) throws TopologyException;

GraphiQL集成

效果图如下:
在这里插入图片描述
为了你应用能与GraphiQL集成,要求在GraphiQL的输入URL后默认的schema查询有正确返回,此时接收GQL语句的查询query,如下所示:
在这里插入图片描述

query{
  __schema{
    types {
      name
      description
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值