Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Draft] Add GraphQL Subscription to ControllerClient #163

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

zhengfeihe
Copy link

Enable GraphQL subscription based on WebSocket

rRequestStringBuffer.Clear();
}

std::string ControllerClientImpl::SpinOnce(){
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure whether we should process the read message on the client side:

  1. Parse the message with RapidJSON .
  2. Skip the keep-alive message {"type":"ka"} and continue reading until we receive a valid message.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is the user's job to call SpinOce, then you don't need the SpinOnce function. SpinOnce is usually used when there is a background thread. In this case, maybe you can consider to have a function GetSubriptionResult or something similar to let the use to fetch the data when they need it.

/// \brief Execute GraphQL subscription query against Mujin Controller.
///
/// Throws an exception if there are any errors
virtual void ExecuteGraphSubscription(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Document::AllocatorType& rAlloc) = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason const char*?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow the ExecuteGraphQuery

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's okay, but better to use mordern C++.

@@ -475,6 +484,38 @@ void ControllerClientImpl::ExecuteGraphQueryRaw(const char* operationName, const
_ExecuteGraphQuery(operationName, query, rVariables, rResult, rAlloc, timeout, false, true);
}

void ControllerClientImpl::ExecuteGraphSubscription(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Document::AllocatorType& rAlloc)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't you have a rapidjson::MemoryPoolAllocator<> in the member, so you don't need to keep passing rAlloc?

Copy link
Author

@zhengfeihe zhengfeihe Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of doing this, but I followed the current deign of ExecuteGraphQuery for consistency, otherwise we need to change both

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Subscription is a frequent job and will lead to some overhead. Better to use the memory pool.

rPayLoad.AddMember(rapidjson::Document::StringRefType("variables"), rValue, rAlloc);
rValue.SetString("start", rAlloc);
rRequest.AddMember(rapidjson::Document::StringRefType("type"), rValue, rAlloc);
rRequest.AddMember(rapidjson::Document::StringRefType("payload"), rPayLoad, rAlloc);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's hard to read. Add some empty line between L496-507.


rapidjson::Writer<rapidjson::StringBuffer> writer(rRequestStringBuffer);
rRequest.Accept(writer);
_pWebsocketStream->write(rRequestStringBuffer.GetString());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check other client implementations to see how to efficiently reuse the buffer.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still, follow the ExecuteGraphQuery for consistency

void ControllerClientImpl::CreateWebSocketStream()
{
boost::asio::io_context io_context;
MUJIN_LOG_INFO(_clientInfo.host);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use a better readable log.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just for test. I will remove it

socket.connect(endpoint);
_pWebsocketStream = std::make_unique<UnixWebSocketStream>(std::move(socket));
}
std::string usernamepassword = _clientInfo.username + ":" + _clientInfo.password;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using boost::format might have slightly better performance.

std::string usernamepassword = _clientInfo.username + ":" + _clientInfo.password;
std::string encodedusernamepassword = GetEncodeBase64String(usernamepassword);
_pWebsocketStream->set_option(usernamepassword, encodedusernamepassword);
_pWebsocketStream->handshake(_clientInfo.host, "/api/v2/graphql");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract the endpoint as a static contexpr std::string.

return message;
}

void TCPWebSocketStream::close() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also protected by boost::mutex::scoped_lock lock(_mutex);?

rRequestStringBuffer.Clear();
}

std::string ControllerClientImpl::SpinOnce(){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is the user's job to call SpinOce, then you don't need the SpinOnce function. SpinOnce is usually used when there is a background thread. In this case, maybe you can consider to have a function GetSubriptionResult or something similar to let the use to fetch the data when they need it.

@kressjh kressjh self-requested a review September 10, 2024 05:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants