I've been looking for some time now, how to implement graphql-subscriptions in c++ without libgraphqlparser, CAFFQL or cppgraphqlgen which are the recommended libraries for graphql implementations.
Currently i am using curl for queries and mutations, which is working so far, but this wont work for subscriptions. In javascript Apollo uses a Websocket for this purpose. Is it possible to use a websocket in c++ to do something similar?
EDIT
The GraphhQL server I am currently connecting to is a 1:1 copy of this tutorial: https://www.howtographql.com/graphql-js/0-introduction/
Here is my implementation:
TestClient.hpp:
#include <string>
#include <list>
#include <curlpp/Easy.hpp>
#include "jsoncpp/json/reader.h"
class TestClient {
public:
TestClient();
virtual ~TestClient();
void login();
void subscribe();
protected:
std::string sendRequest(const std::list<std::string> &header, const std::string &graphQL);
std::string loadFile(std::string filePath);
private:
curlpp::Easy *m_request = new curlpp::Easy();
Json::Reader m_reader;
std::string m_token = "";
std::string m_grapqlLogin;
std::string m_grapqlSubscribe;
const std::string m_URL = "http://localhost:4000/graphiql";
const std::string m_headerContentType = "Content-Type: application/json";
const std::string m_headerToken = "Authorization:Bearer ";
};
TestClient.cpp:
#include "TestClient.hpp"
#include <boost/log/trivial.hpp>
#include <curlpp/cURLpp.hpp>
#include <curlpp/Options.hpp>
#include <curlpp/Easy.hpp>
#include <curlpp/Exception.hpp>
#include <fstream>
#include <exception>
#include <regex>
using namespace std;
using namespace curlpp;
TestClient::TestClient()
{
m_grapqlLogin = loadFile("./data/login.graphql");
m_grapqlSubscribe = loadFile("./data/subscribe.graphql");
}
void TestClient::login()
{
list<string> header = { m_headerContentType };
Json::Value json;
string result = sendRequest(header, m_grapqlLogin);
if(m_reader.parse(result, json, false))
{
if(json["errors"] == Json::nullValue)
{
cout << json.toStyledString() << endl;
m_token = json["data"]["login"]["token"].asString();
}
}
}
void TestClient::subscribe()
{
string headerToken = m_headerToken + m_token;
std::list<string> header = { m_headerContentType, headerToken };
Json::Value json;
string result = sendRequest(header, m_grapqlSubscribe);
if(m_reader.parse(result, json, false))
{
if(json["errors"] == Json::nullValue)
{
cout << json.toStyledString() << endl;
}
}
}
string TestClient::sendRequest(const std::list<string> &header, const string &graphQL)
{
stringstream result;
m_request->reset();
m_request->setOpt<Options::Url>(m_URL);
m_request->setOpt<Options::Post>(true);
m_request->setOpt<Options::HttpHeader>(header);
m_request->setOpt<Options::PostFields>(graphQL);
m_request->setOpt<Options::PostFieldSize>(graphQL.size());
m_request->setOpt<Options::WriteStream>(&result);
m_request->perform();
return result.str();
}
The output of this code is:
{
"data" : {
"login" : {
"token" : "a long token",
"user" : {
"email" : "alice#prisma.io",
"links" : [
{some links}
]
}
}
},
"errors" : null
}
{
"data" : {
"newLink" : null
},
"errors" : null
}
Now I don't know how to use curlpp together with subscricptions. Has someone an idea? Thank you very much.
Related
I was trying to put this code into a class but I couldn't manage to do it. The job of the function is pulling team names from a .txt file and putting them in a vector. I think the main problem is I couldn't select the right function return type.
This is the teams.txt: (The names before the "-" symbol are teams. Other names are unrelated with my question but they are coachs of the teams.)
Trabzonspor-Abdullah Avcı+
Fenerbahçe-Vítor Pereira+
Beşiktaş-Sergen Yalçın+
Galatasaray-Fatih Terim+
İstanbul Başakşehir-Emre Belözeoğlu+
Alanyaspor-Bülent Korkmaz+
Fatih Karagümrük-Francesco Farioli+
Gaziantep-Erol Bulut+
Adana Demirspor-Vincenzo Montella+
Ankara Dinc-Nestor El Maestro+
Antalyaspor-Nuri Şahin+
Kayserispor-Hikmet Karaman+
Yeni Malatyaspor-Marius Sumudica+
Konyaspor-İlhan Palut+
Sivasspor-Rıza Çalımbay+
Hatayspor-Ömer Erdoğan+
Giresunspor-Hakan Keleş+
Kasımpaşa-Hakan Kutlu+
And this is the my code who does the putting them in a vector:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std; //I know that's a bad practice but i just need to do this for while
std::string process(std::string const& s) //A function to seperate teams from the coaches
{
string::size_type pos = s.find('-');
if (pos != string::npos)
{
return s.substr(0, pos);
}
else
{
return s;
}
}
int main() {
ifstream readTeam("teams.txt");
if (!readTeam) { //checking that successfully opened the file.
std::cerr << "Error while opening the file.\n";
return 1;
}
vector<std::string> teams;
string team;
while (getline(readTeam, team)) {
teams.push_back(process(team));
}
readTeam.close();
int g = 1;//for printing the teams, just for displaying it. doesn't have to in a class.
for (const auto& i : teams) {
cout << g;
cout << i << endl;
g++;
}
return 0;
}
And that's what i did(tried) to make it a class:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
std::string process(std::string const& s)
{
string::size_type pos = s.find('-');
if (pos != string::npos)
{
return s.substr(0, pos);
}
else
{
return s;
}
}
class readFile {
public:
void setTxtName(string);
vector<unsigned char> const& getTeam() const{
}
vector<string> teams;
private:
string fileName;
};
int main() {
readFile pullTeams;
pullTeams.setTxtName("teams.txt");
return 0;
}
void readFile::setTxtName(string txtName) {
fileName = txtName;
}
vector<string> const& readFile::getTeam { //problem is defining it(I think). So I couldn't add my main code int it..
return teams;
}
Anything helps, thank you!
I did a little different research based on the Botje's comment. And I manage to create an answer based on here. Thanks.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
std::string process(std::string const& s){
string::size_type pos = s.find('-');
if (pos != string::npos){
return s.substr(0, pos);
}
else{
return s;
}
}
class readFile {
public:
vector<string> getTxt();
bool read(string);
private:
vector<string> teams;
string team;
ifstream txt;
};
int main() {
vector<string> teams;
readFile team;
if (team.read("teaams.txt") == true)
teams = team.getTxt();
int g = 1;//for printing the teams, just for displaying it. doesn't have to in a class.
for (const auto& i : teams) {
cout << g;
cout << i << endl;
g++;
}
return 0;
}
bool readFile::read(string txtName) {
ifstream txt;
string team;
txt.open(txtName.c_str());
if (!txt.is_open())
return false;
while (getline(txt, team))
teams.push_back(process(team));
return true;
}
vector<string> readFile::getTxt() {
return teams;
}
#include "json.hpp"
#include <memory>
#include <vector>
#include <iostream>
struct json_node;
using json_node_ptr = std::shared_ptr<json_node>;
struct json_node
{
int id;
std::vector<json_node_ptr> children;
json_node(int _id)
: id{ _id }
{
}
};
void to_json(nlohmann::json& j, const json_node_ptr& node)
{
j = {{"ID", node->id}};
if (!node->children.empty()) {
j.push_back( nlohmann::json {"children", node->children});
//j["children"] = node->children;
}
}
int main()
{
}
I am getting the following error. How do I resolve this? What is the problem behind it?
Any easy workaround? It is not easy to change client library.
error: no matching function for call to ‘basic_json<>::push_back(<brace-enclosed initializer list>)’
j.push_back( {"children", node->children} );
Header file is here: https://github.com/nlohmann/json/blob/v2.0.10/src/json.hpp
UPDATE: THIS PROBLEM OCCURS WITH OLDER VERSIONS OF THE LIBRARY. IT IS
FIXED IN LATEST VERSION.
Here the code that converts the struct to string and then parses string back to json object
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <string>
#include "json.hpp"
struct json_node;
using json_node_ptr = std::shared_ptr<json_node>;
struct json_node
{
int id;
std::vector<json_node_ptr> children;
json_node(int _id)
: id{ _id }
{
}
std::ostringstream& print(std::ostringstream& ss) const
{
ss << "{\"ID\" :" << id ;
if (children.size() > 0) {
ss << ", \"children\" : [";
std::string prefix = "";
for (auto& ch : children) {
ss << prefix;
ch->print(ss);
prefix = ", ";
}
ss << "]";
}
ss << "}";
return ss;
};
};
std::ostringstream& operator<<(std::ostringstream& ss,const json_node & node)
{
return node.print(ss);
}
void to_json(nlohmann::json& j, const json_node_ptr& node)
{
std::ostringstream ss;
ss << *node;
std::string str = ss.str();
j = nlohmann::json::parse(str);
}
int main()
{
json_node_ptr p = std::make_shared<json_node>(1);
json_node_ptr child_1 = std::make_shared<json_node>(2);
p->children.push_back(child_1);
json_node_ptr child_2 = std::make_shared<json_node>(3);
json_node_ptr child_3 = std::make_shared<json_node>(4);
child_2->children.push_back(child_3);
p->children.push_back(child_2);
nlohmann::json j;
to_json(j, p);
std::cout << j << '\n';
nlohmann::json j1 = nlohmann::json::array();
j1.push_back(j);
j1.push_back(p);
std::cout << j1 << "\n";
}
Output
{"ID":1,"children":[{"ID":2},{"ID":3,"children":[{"ID":4}]}]}
Here is another vesion of the to_json - that does not use to string serialization
void to_json(nlohmann::json& j, const json_node_ptr& node)
{
j["ID"] = node->id;
if (node->children.size() > 0)
{
j["children"] = nlohmann::json::array();
for (auto& ch : node->children)
{
nlohmann::json j_child;
to_json(j_child, ch);
j["children"].push_back(j_child);
}
}
}
i am using the proxygen for making a simple web server. i am restricted to use proxygen.I am using proxygen default echo server example i want to print the header values whenever a request is sent to the server. below is the code that i think i should modify.but what exactly i am unsure of.:
#include "customHandler.h"
#include <iostream>
#include <proxygen/httpserver/RequestHandler.h>
#include <proxygen/httpserver/ResponseBuilder.h>
#include <proxygen/lib/http/HTTPMessage.h>
#include <proxygen/lib/http/HTTPMethod.h>
using namespace std;
using namespace proxygen;
namespace EchoService {
EchoHandler::EchoHandler(EchoStats* stats): stats_(stats) {
}
void EchoHandler::onRequest(std::unique_ptr<HTTPMessage> headers) noexcept {
//------------------HERE TO MODIFY I THINK-------------------//
}
void EchoHandler::onBody(std::unique_ptr<folly::IOBuf> body) noexcept {
if (body_) {
body_->prependChain(std::move(body));
} else {
body_ = std::move(body);
}
}
/*
.header("Request-Number",
//this sets the request number
folly::to<std::string>(stats_->getRequestCount()),"test-b")*/
void EchoHandler::onEOM() noexcept {
ResponseBuilder(downstream_)
.status(200, "OK")
//Response is set Here...........ex-> .body(std::move("some Response object"))
.body(std::move(body_))
.sendWithEOM();
}
void EchoHandler::onUpgrade(UpgradeProtocol protocol) noexcept {
// handler doesn't support upgrades
}
void EchoHandler::requestComplete() noexcept {
delete this;
}
void EchoHandler::onError(ProxygenError err) noexcept {
delete this;
}
}
correct me if i am wrong.
Try this:
void EchoHandler::onRequest(std::unique_ptr<HTTPMessage> headers) noexcept
{
HTTPHeaders head = headers->getHeaders();
head.forEach([&] (const string& header, const string& val) {
cout << header << ": " << val<<endl;
});
}
I have an error when I print my PrintSount() in the main. The compiler said:
No member named 'PrintSound' in 'animal')
Why is this happening and how I can fix it?
main
#include <iostream>
#include "animal.h"
#include "pat.h"
#include "not_pat.h"
int main()
{
std::string lastName;
std::string sound;
std::string name;
std::string type;
double speed = 0;
animal* animals[2];
for (int i = 0; i < 2; i++)
{
std::string ani;
std::cout << "Enter animal: (dog,fish,lion,monkey ... ): " << std::endl;
std::cin >> ani;
if (ani == "dog" || ani == "cat" || ani == "fish")
{
animals[i] = new pat("test", "test", "test","test");
}
else
{
animals[i] = new not_pat(speed, lastName, "test2", "test2");
}
}
for (int i = 0; i < 2; i++)
{
std::cout << animals[i]->PrintName() << animals[i]->PrintLatName() << animals[i]- >PrintType() << animals[i]->PrintSound() << std::endl;
}
}
animal.h
#ifndef ANIMAL_H
#define ANIMAL_H
#include <iostream>
#include <stdio.h>
#include <string>
class animal{
public:
animal(std::string name, std::string type, std::string lastName);
std::string PrintType();
std::string PrintName();
std::string PrintLatName();
protected:
std::string _name;
std::string _lastName;
std::string _type;
private:
};
#endif
animal.cpp
#include "animal.h"
animal::animal(std::string name , std::string type, std::string lastName)
{
_name = name;
_type = type;
_lastName = lastName;
}
std::string animal::PrintType()
{
return this->_type;
}
std::string animal::PrintName()
{
return this->_name;
}
std::string animal::PrintLatName()
{
if(_lastName == "0")
{
return NULL;
}
else
{
return this->_lastName;
}
}
pat.h
#ifndef PAT_H
#define PAT_H
#include "animal.h"
#include <iostream>
#include <stdio.h>
#include <string>
class pat : public animal
{
public:
pat(std::string lastName, std::string sound, std::string name, std::string type);
std::string PrintSoud(animal *p);
protected:
std::string _sound;
private:
};
#endif
pat.cpp
#include "pat.h"
#include "animal.h"
pat::pat(std::string lastName, std::string sound, std::string name, std::string type) : animal(name,type,lastName)
{
_sound = sound;
}
std::string pat::PrintSoud(animal *p)
{
return this->_sound;
}
not_pat.h
#ifndef NOT_PAT_H
#define NOT_PAT_H
#include <iostream>
#include <stdio.h>
#include <string>
class not_pat : public animal
{
public:
not_pat(double speed,std::string lastName, std::string name, std::string type);
double PrintSpeed();
protected:
double _speed;
private:
};
#endif
not_pat.cpp
#include "animal.h"
#include "not_pat.h"
not_pat::not_pat(double speed, std::string lastName, std::string name, std::string type) : animal(name, type,lastName)
{
if(speed == 0)
{
_speed = NULL;
}
else
{
_speed = speed;
}
}
double not_pat::PrintSpeed()
{
return this->_speed;
}
C++ is a statically-typed language. You use pat using a pointer to animal. So the compiler checks if animal has the member function PrintSound(). It does not have it, so there is a compilation error raised. You need to add the declaration of PrintSound to animal (probably a pure virtual function) to fix this.
There is the following code. The Test function can either accept or not a slot as the parameter. Is there a way to achieve the same but using only the boost::signal2 library?
#include <boost/signals2.hpp>
#include <boost/optional.hpp>
#include <string>
#include <iostream>
typedef boost::signals2::signal<std::string (void)> CSignal;
typedef CSignal::slot_type CSlotType;
typedef boost::optional<CSlotType> COptSlotType;
void Test(const COptSlotType &optSlot = COptSlotType()) {
std::string str;
if (optSlot) {
CSignal sig;
sig.connect(*optSlot);
const auto rt = sig();
if (rt) {
str = *rt;
}
}
else {
str = "No Slot";
}
std::cout << str << std::endl;
}
std::string TestSignal(void) {
return "Signal";
}
int main(int argc, char *argv[]) {
Test();
Test(COptSlotType(&TestSignal));
return 0;
}