/// <summary>
        /// Run a query against Nebulon ON
        /// </summary>
        /// <typeparam name="T">The Type of the target array</typeparam>
        /// <param name="name">Name of the query</param>
        /// <param name="parameters">Parameters for the query</param>
        /// <returns>List of items returned by the query</returns>
        public T[] RunQueryMany <T>(string name, GraphQLParameters parameters = null) where T : new()
        {
            try
            {
                // field types for objects are all marked with a property attribute
                // for all supported Neb* types. Every field that should be queried for
                // must be provided in the class properties as JsonPath attribute.
                string[] fields = GetQueryFields(typeof(T));

                // this composes a new GraphQL method of type query.
                GraphQLMethod method = new GraphQLMethod(
                    GraphQLMethodType.Query,
                    name,
                    parameters,
                    fields
                    );

                // run the task
                object[] taskResults = Run <T>(method);

                // this is the most tricky part here as the type conversion from a generic
                // type to something else doesn't seem to be very reliable. However,
                // this approach seeps to work fine (according to Stack Overflow).
                return(CastObjects <T>(taskResults));
            }
            catch (AggregateException errors)
            {
                string errorMessage = "Error executing query";

                foreach (Exception err in errors.InnerExceptions)
                {
                    errorMessage = string.Concat(errorMessage, ", ", err.Message);
                }

                Logger.WriteError(errorMessage);

                throw new Exception(errorMessage);
            }
            catch (TimeoutException err)
            {
                Logger.WriteError(err.Message);
                throw new Exception(err.Message);
            }
            catch (Exception err)
            {
                Logger.WriteError(err.Message);
                throw new Exception(err.Message);
            }
        }
        /// <summary>
        /// Execute a GraphQL method against nebulon on.
        /// </summary>
        /// <typeparam name="T">Type of the object that we expect as a response</typeparam>
        /// <param name="method">GraphQL method</param>
        /// <returns></returns>
        private object[] Run <T>(GraphQLMethod method) where T : new()
        {
            // this could be made static as this URI should never really change.
            // the server doesn't end in a "/", but I think we should eventually
            // check for a malformed server.
            string uri = string.Concat(this._server, "/query");

            Logger.WriteDebug(string.Format(
                                  "Sending '{0}' to {1}.",
                                  method,
                                  uri
                                  ));

            // prepare the request body. We need to make a JSON body
            // for UCPAI using JSON object with a single property "query".
            JObject jsonObject = new JObject(new JProperty(@"query", method.ToString()));
            string  bodyString = JsonConvert.SerializeObject(jsonObject, Formatting.Indented);

            // we want to keep track of how long this request took, so we keep track
            // of the start time. If we later don't care about it anymore, we can
            // delete this.
            DateTime start = DateTime.UtcNow;

            HttpContent content = new StringContent(
                bodyString,
                Encoding.UTF8,
                @"application/json"
                );

            content.Headers.Add("Nebulon-Client-App", _assemblyVersion);
            content.Headers.Add("Nebulon-Client-Platform", _platform);

            HttpResponseMessage response = Post(uri, content);
            string responseString        = ReadResponseBodyString(response);

            // this is the earliest we should stop recording the time / duration of the
            // request as this is mostly now on the client side, not the server.
            double durationMs = (DateTime.UtcNow - start).TotalMilliseconds;

            Logger.WriteDebug(string.Format("Got response '{0}' in {1} ms.",
                                            response.StatusCode,
                                            durationMs));
            Logger.WriteDebug(string.Concat("Response:", responseString));

            // the UCAPI server response with JSON formatted data
            // with a well known structure. one property is "errors" and is
            // present when the request failed, and another property "data"
            // which is populated when the request succeeds.
            JObject json = JObject.Parse(responseString);

            // check for errors (any HTTP code outside of 2xx). We probably want to still
            // get the response body and bubble that up in the exception
            int statusCode = (int)response.StatusCode;

            if (200 < statusCode || statusCode >= 300 || json.ContainsKey("errors"))
            {
                string errorMessage = string.Format("Request error (HTTP {0})",
                                                    statusCode);

                if (json.ContainsKey("errors"))
                {
                    // get the errors that are specified in the response JSON
                    // and add them to the error string.
                    IEnumerable <JToken> errors = json.SelectTokens("errors[*].message", true);

                    foreach (JToken error in errors)
                    {
                        // convert the JToken to a string and append it to the
                        // error message
                        string errorString = (string)error.ToObject(typeof(string));
                        errorMessage = string.Concat(errorMessage, ", ", errorString);
                    }
                }

                throw new Exception(errorMessage);
            }

            // read data. This query could fail when the data is not present in the
            // response. So, we're checking for the value to not be null.
            string jsonPathData = string.Concat("$.data.", method.MethodName);
            JToken data         = json.SelectToken(jsonPathData, false);

            if (data == null)
            {
                return(null);
            }

            if (data.Type != JTokenType.Array)
            {
                if (data.Type == JTokenType.Object)
                {
                    JObject resultData = data.Value <JObject>();
                    return(new object[] { ObjectFromJObject(resultData, typeof(T)) });
                }

                // Convert primitive types directly
                return(new object[] { data.Value <T>() });
            }

            JArray        dataArray       = data.Value <JArray>();
            List <object> resultDataArray = new List <object>();

            foreach (JObject item in dataArray)
            {
                resultDataArray.Add(ObjectFromJObject(item, typeof(T)));
            }

            return(resultDataArray.ToArray());
        }