0

在这里,我将讨论 GraphQL 在我的 Project 数据结构中的实现。

在我的项目中,我使用平面文件(文本文件)固定宽度格式进行数据库管理。我想将 API 开发与它集成。从 CPP 程序查询的平面文件。在这种情况下,我很难为 CPP Node Addon Native modules (OR) Nodejs NPM module GraphQL 选择 GraphQL API。

案例 1:有没有办法为NPM GraphQL 包使用平面文件配置?

案例 2:有没有办法使用 node-addon-api 编写 Node Native 模块?(对于 CPP 开发人员使用来自 github 中名为cppgraphqlgen的开源项目的 GraphQL 解析器)

如果我选择第二种情况,我将面临以下问题。

我正在尝试将 cppgraphqlgen 服务编写为节点插件本机模块。我提到了更多关于那个的话题。在我的项目中,一些模块已经使用Napi.h在 CPP Node 插件 Native 模块中编写。在这个用不同 API 编写的 cppgraphqlgen 项目中,比如nan.hcppgraphiql使用 Nan。我很难将这些服务编写为我的项目的节点插件。

在我的项目中制作文件如下:

CFLAGS = -Os -O3 -fPIC -Ofast -std=gnu++17

CC = gcc
CXX = g++

LDFLAGS := \
    -pthread \
    -rdynamic

DEFS := \
    '-DUSING_UV_SHARED=1' \
    '-DUSING_V8_SHARED=1' \
    '-DV8_DEPRECATION_WARNINGS=1' \
    '-DV8_DEPRECATION_WARNINGS' \
    '-DV8_IMMINENT_DEPRECATION_WARNINGS' \
    '-D_LARGEFILE_SOURCE' \
    '-D_FILE_OFFSET_BITS=64' \
    '-DOPENSSL_NO_PINSHARED' \
    '-DOPENSSL_THREADS' \
    '-D__STDC_FORMAT_MACROS' \
    '-DNAPI_DISABLE_CPP_EXCEPTIONS' \
    '-DBUILDING_NODE_EXTENSION'

# Flags passed to all source files.
GYP_CFLAGS := \
    -frtti \
    -fexceptions \
    -pthread \
    -Wall \
    -Wextra \
    -Wno-unused-parameter \
    -fno-omit-frame-pointer

# Flags passed to only C files.
CFLAGS_C :=

# Flags passed to only C++ files.
CFLAGS_CC := \
    -std=gnu++1y \
    -fexceptions

INC_NAPI := -I/usr/include/node-addon-api
INC_NODE := -I/usr/include/node

CGI_LIB = -lcgicc

LIBS := ../cppgraphqlgen/src

NOTH_A = nothing.a


OBJ = nothing.o ./hello/HelloClient.o

ENODE = ${NOTH_A} ./hello/HelloClient.node

# GREET_DEMO = ./greet-demo
# CLASS_DEMO = ./class-demo
DEMO := ./today
HELLO := ./hello
target: ${OBJ} ${ENODE}

clean:
    rm -rf *.o *.node *.a
    rm -rf ${DEMO}/*.o ${DEMO}/*.node
    rm -rf ${HELLO}/*.o ${HELLO}/*.node

# ./hello/hello: ./hello/hello.cpp
#   ${CXX} ${CFLAGS} $< -o $@

./hello/hello:
    ${CXX} ${CFLAGS} ./hello/hello.cpp -shared -Wl,-soname=hello.node -Wl,--start-group -L${LIBS}/separategraphql.a  -L${LIBS}/graphqljson.a  -L${LIBS}/separateschema.a  -L${LIBS}/graphqlintrospection.a  -L${LIBS}/graphqlservice.a  -L${LIBS}/graphqlresponse.a  -L${LIBS}/graphqlpeg.a -lpthread ${NOTH_A} -Wl,--end-group -o $@

./hello/HelloClient.o: ./hello/HelloClient.cpp
    ${CXX} ${CFLAGS} '-DNODE_GYP_MODULE_NAME=HelloClient' ${DEFS} ${GYP_CFLAGS} ${CFLAGS_CC} ${INC_NAPI} ${INC_NODE} $< -o $@

./hello/HelloClient.node: ./hello/HelloClient.o
    ${CXX} ${CFLAGS} -shared -Wl,-soname=HelloClient.node -Wl,--start-group ./hello/HelloClient.o -L${LIBS}/separategraphql.a  -L${LIBS}/graphqljson.a  -L${LIBS}/separateschema.a  -L${LIBS}/graphqlintrospection.a  -L${LIBS}/graphqlservice.a  -L${LIBS}/graphqlresponse.a  -L${LIBS}/graphqlpeg.a -lpthread ${NOTH_A} -Wl,--end-group -o $@

nothing.a: nothing.o
    ar crs nothing.a $<

nothing.o: nothing.c
    ${CC} ${LDFLAGS} ${INC_NODE} ${DEFS} -c $< -o $@ 

在 cppgraphqlgen 中有两个名为 schemegen 和 clientgen 的服务。上述两项服务均已成功构建。此实用程序使用 CMake(需要 GNU 10.3.0)。但在我的项目中有实用程序 GNU Make。我的 make 实用程序构建命令不会使用 node-addon-api (Napi.h) 编译 cppgraphqlgen 项目的示例程序。我想要一个用于 cppgraphqlgen 项目的简单入门程序。

例如:

使用 schemagen 服务,自动生成的文件是 HelloSchema.cpp 和 HelloSchema.h

使用 clientgen 服务,自动生成的文件是 HelloClient.cpp 和 HelloClient.h

我将 HelloClient.cpp 更改为节点插件本机

HelloClient.cpp

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

// WARNING! Do not edit this file manually, your changes will be overwritten.

#include <napi.h>
#include "HelloClient.h"

#include <algorithm>
#include <array>
#include <stdexcept>
#include <sstream>
#include <string_view>

using namespace std::literals;

namespace graphql::client {

using namespace query::Query;

namespace query::Query {

const std::string& GetRequestText() noexcept
{
    static const auto s_request = R"gql(
        # Copyright (c) Microsoft Corporation. All rights reserved.
        # Licensed under the MIT License.
        
        query {
            hello
        }
    )gql"s;

    return s_request;
}

const peg::ast& GetRequestObject() noexcept
{
    static const auto s_request = []() noexcept {
        auto ast = peg::parseString(GetRequestText());

        // This has already been validated against the schema by clientgen.
        ast.validated = true;

        return ast;
    }();

    return s_request;
}

Response parseResponse(response::Value&& response)
{
    Response result;

    if (response.type() == response::Type::Map)
    {
        auto members = response.release<response::MapType>();

        for (auto& member:members)
        {
            if (member.first == R"js(hello)js"sv)
            {
                result.hello = ModifiedResponse<response::StringType>::parse<TypeModifier::Nullable>(std::move(member.second));
                continue;
            }
        }
    }

    return result;
}

} // namespace query::Query
} // namespace graphql::client

Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
    // auto expected = "Test String";
    // auto actual = graphql::response::Value(expected);
    // std::string res = graphql::client::query::Query::parseResponse(actual);

    exports.Set("test", "test"); // <--- Here How to call and get the result "Hello World!"

    return exports;
}

NODE_API_MODULE(NODE_GYP_MODULE_NAME, InitAll);

输入架构

type Query {
    hello: String
}

输入查询:

{hello}

输出如:

{"data":{"hello":"Hello World!"}}

另一个尝试在名为 today 的项目文件夹中的示例程序(使用 TodayMock 的 sample.cpp。我尝试编写 HelloMock)

HelloMock.h

#pragma once

#ifndef HELLOMOCK_H
#define HELLOMOCK_H

#include "HelloSchema.h"

#include <atomic>
#include <stack>

namespace graphql::hello
{
    class Query
    {
        public:
            using greetMsg = std::function<std::optional<response::StringType>>();
            explicit Query(greetMsg &&getHello);

            virtual service::FieldResult<std::optional<response::StringType>> getHello() final;
    };
}

#endif // HELLOMOCK_H

HelloMock.cpp

#include "HelloMock.h"

#include <algorithm>
#include <iostream>

namespace graphql::hello
{
    
    Query::Query(greetMsg &&getHello)
    {
    }
    service::FieldResult<std::optional<response::StringType>> Query::getHello()
    {
        return "Hello World";
    }

} // namespace graphql::hello

你好.cpp

#include "HelloMock.h"

#include "graphqlservice/JSONResponse.h"
#include <cstdio>
#include <iostream>
#include <iterator>
#include <stdexcept>

using namespace graphql;

int main(int argc, char **argv)
{
    /* From HelloMock*/
    response::StringType helloKey;

    std::string fakeHelloKey("hello");
    helloKey.resize(fakeHelloKey.size());
    std::copy(fakeHelloKey.cbegin(), fakeHelloKey.cend(), helloKey.begin());
    
    std::cout << "Created the service..." << std::endl;

    auto query = std::make_shared<hello::Query>(
        [&fakeHelloKey]() -> std::optional<response::StringType>
        {
            std::cout << "Called getHelloMsg..." << std::endl;
            return std::optional<response::StringType>();
        }
    );
        
    auto service = std::make_shared<hello::Operations>(query);
    std::cout << "Created the service..." << std::endl;

    try
    {
        peg::ast query;

        if (argc > 1)
        {
            query = peg::parseFile(argv[1]);
        }
        else
        {
            std::istream_iterator<char> start{std::cin >> std::noskipws}, end{};
            std::string input{start, end};

            query = peg::parseString(std::move(input));
        }

        if (!query.root)
        {
            std::cerr << "Unknown error!" << std::endl;
            std::cerr << std::endl;
            return 1;
        }

        std::cout << "Executing query..." << std::endl;

        std::cout << response::toJSON(service
                                          ->resolve(nullptr,
                                                    query,
                                                    ((argc > 2) ? argv[2] : ""),
                                                    response::Value(response::Type::Map))
                                          .get())
                  << std::endl;
    }
    catch (const std::runtime_error &ex)
    {
        std::cerr << ex.what() << std::endl;
        return 1;
    }

    return 0;
}

NodeJS程序:

var express = require('express');
var {
    graphqlHTTP
} = require('express-graphql');
var {
    buildSchema
} = require('graphql');

var schema = buildSchema(`
  type Query {
    hello: String
  }
`);

var root = {
    hello: () => 'Hello world!'
};

var app = express();
app.use('/graphql', graphqlHTTP({
    schema: schema,
    rootValue: root,
    graphiql: true,
}));
app.listen(4000, () => console.log('Now browse to localhost:4000/graphql'));

如果有办法在 GraphQL API 服务中使用平面文件。请告诉我。我想为我的项目急切地使用 GraphQL API 编写一个较新的实现。以及如何在第二个clientgen服务中使用和实现node addon?客户端有什么用?请帮我。

4

1 回答 1

1

我们一直在https://github.com/microsoft/cppgraphqlgen/issues/191讨论这个问题,我认为大部分构建问题已经解决。

我们还没有谈到的一件事是clientgenhttps://github.com/microsoft/cppgraphqlgen的目的。这是为了生成已编译的 GraphQL 查询,以防您想从 C++ 调用 GraphQL 服务。对于您的方案,您可能不需要担心这一点,因为您正在谈论从平面文件公开 GraphQL 服务。如果您有用于访问文件的 C 或 C++ API,schemagen(和graphqlservice/graphqlintrospection库)应该是您所需要的。

于 2022-01-09T21:37:14.087 回答