Executing GraphQL Queries
This guide shows how to execute GraphQL queries and mutations against Exacta Maestro™'s schema.
Endpoints
Many of the microservices in a deployed environment of Exacta Maestro™ expose their own GraphQL schema over HTTPS through Bastian Solutions's load balancer. While the address of the load balancer changes for each deployed environment, every service that exposes a GraphQL server exposes the following endpoints:
https://{load-balancer-address}/{clientId}/{environmentId}/{serviceName}/graphql
https://{load-balancer-address}/{clientId}/{environmentId}/{serviceName}/graphql/ui
Tip
The clientId
, environmentId
, and serviceName
are all case-sensitive.
The values for load-balancer-address, clientId, and environmentId will be distributed during system setup, while the serviceNames are constant within Exacta Maestro™. The /graphql
path is for posting queries to the service's GraphQL schema, making it the primary endpoint for production workflows. The /graphql/ui
endpoint exposes a GUI for schema exploration and administrative query execution.
Service | serviceName | Endpoints |
---|---|---|
Archive | /archive | Cold storage of old data |
Agent Hub | /agentHub | Agent Data Queries, Environment Setup |
Aliasing | /aliasing | Environment Setup |
Inventory | /inventory | Manual Inventory Interaction, Environment Setup |
LogHub | /logHub | Log aggregation and export |
Map | /map | Environment Setup |
Task Assignment | /taskAssignment | Task Creation, Task Queries, Assignment Algorithm Configuration |
WebHmi | /webhmi | Web dashboard GUI for Exacta Maestro™ |
Tip
Some deployments may contain an extra GraphQL Gateway application, which exposes a single HTTPS endpoint over the load balancer and forwards any received request to the service targeted by the request body. The Gateway does not expose either GUI application nor any schema documentation, it just forwards the requests received on its main /graphql
endpoint.
Important
As the GraphQL Gateway was developed for backwards compatibility, it is capable of circumventing client authentication. This requires the configuration of an encrypted password, used by the Gateway to resolve a valid JWT from Bastian.Identity, which the Gateway adds to the headers of any unauthenticated requests before forwarding.
Authentication
All of Exacta Maestro™'s GraphQL services require authentication to use. For more information on this topic, see Authenticating with Exacta Maestro™. To avoid detracting from the GraphQL content, the following examples assume you have access to a valid JWT token.
Raw HTTP Request
GraphQL Requests are made with an HTTP POST. These headers are required:
Host
: https://{load-balancer-address}/{clientId}/{environmentId}/graphqlContent-Type
: application/jsonAuthorization
: Bearer $JWT
The POST body is a JSON object with the following structure:
{
"Query" :"",
"OperationName": "",
"Variables": {}
}
Query: The full text of the GraphQL query to execute. Since GraphQL is a language of its own, this is a string rather than a JSON object.
OperationName: The name of the operation to execute. This can be omitted if the query contains a single operation.
Variables: A JSON object containing any variables defined in the query's operation. The keys in the root of this object will be the variable names. The variable values will be scalars or objects mapping to the GraphQL type defined for the variable. If no variables were declared in the operation, this field can be omitted.
Here is an example of a raw POST to create a Task Group in Exacta Maestro™. This example was originally created in PostMan and traced with Fiddler.
POST https://localhost/clientId/environmentId/taskAssignment/graphql HTTP/1.1
Host: localhost:81
Connection: keep-alive
Content-Length: 315
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36
Cache-Control: no-cache
Origin: chrome-extension://fhbjgbiflinjbdggehcddcbncdddomop
Postman-Token: f64c72c1-54c3-9374-d698-8a8d3d489c6e
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Authorization: Bearer $JWT
{
"Query": "mutation newTaskGroupOperation($input:TaskGroupInput!) { taskAssignmentMutation { newTaskGroup(request:$input) { isSuccess error } } }",
"OperationName": "newTaskGroupOperation",
"Variables": {
"input": {
"name": "My Task Group"
}
}
}
With the UI Playground
Visit https://{load-balancer-address}/{clientId}/{environmentId}/{serviceName}/graphql/ui
. The displayed page contains an editor where you can type your query with intellisense support, as well as an explorer for viewing the schema.
Authentication information is required in order to use the UI Playground. Click the settings icon in the top right corner, ensure the Schema Endpoint
matches the intended service's /graphql
endpoint, and in the Autheorization tab provide a valid Bearer token.
With Powershell
GraphQL requests can be made with Windows Powershell v3 and higher using the Invoke-RestMethod
cmdlet.
$body = @{
query = @'
mutation newTaskGroupOperation($taskGroupName:String!) {
taskAssignmentMutation {
newTaskGroup(request: { name: $taskGroupName }) {
isSuccess
error {
key
collectionName
message
}
value {
id
name
dateCreated
taskGroupState
}
}
}
}
'@
operationName = "newTaskGroupOperation"
variables = @{
taskGroupName = "My Task Group"
}
}
$jsonBody = ConvertTo-Json $body -Depth 10
$headers = @{
'Authorization' = "Bearer $JWT"
}
$result = Invoke-RestMethod -Method 'Post' -Uri $GraphQLConfig.uri -Body $jsonBody -Headers $headers -ContentType "application/json"
With C#
You can use .NET's HttpClient class to post a GraphQL query. HttpClient is available since .NET 4.5 and in all editions of .NET Core. This example also uses the NewtonSoft.Json library to serialize the request and deserialize the response.
using Newtonsoft.Json;
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace HttpClientTest
{
class Program
{
static readonly HttpClient httpClient = new HttpClient();
static async Task Main(string[] args)
{
var body = new
{
Query = @"mutation newTaskGroupOperation($taskGroupName:String!) {
taskAssignmentMutation {
newTaskGroup(request: { name: $taskGroupName }) {
isSuccess
error {
key
collectionName
message
}
value {
id
name
dateCreated
taskGroupState
}
}
}
}",
OperationName = "newTaskGroupOperation",
Variables = new
{
taskGroupName = "My Task Group 2"
}
};
var jsonBody = JsonConvert.SerializeObject(body);
using var stringContent = new StringContent(jsonBody,
Encoding.UTF8,
"application/json");
using var httpMessage = new HttpRequestMessage(HttpMethod.Post,
"https://localhost/clientId/environmentId/taskAssignment/graphql");
httpMessage.Headers.Add("Authorization",
"Bearer $JWT");
httpMessage.Content = stringContent;
using var httpResponse = await httpClient.SendAsync(httpMessage);
if(httpResponse.StatusCode == HttpStatusCode.OK)
{
var responseBody = await httpResponse.Content.ReadAsStringAsync();
dynamic graphQLResponse = JsonConvert.DeserializeObject(responseBody);
var taskGroupId = graphQLResponse.data.taskAssignmentMutation.createTaskGroup.id;
Console.WriteLine(taskGroupId.ToString());
}
}
}
}