kubos_app/
query.rs

1/*
2 * Copyright (C) 2018 Kubos Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use failure::format_err;
18use kubos_system::Config as ServiceConfig;
19
20use std::time::Duration;
21
22/// The result type used by `query`
23type AppResult<T> = Result<T, failure::Error>;
24
25/// Execute a GraphQL query against a running KubOS Service using UDP.
26///
27/// Returns the parsed JSON result as a serde_json::Value on success
28///
29/// # Arguments
30///
31/// * `config` - The configuration information for the service which should be queried
32/// * `query` - The raw GraphQL query as a string
33/// * `timeout` - The timeout provided to the UDP socket. Note: This function will block when `None`
34///               is provided here
35///
36/// # Examples
37///
38/// ```
39/// # use failure;
40/// use kubos_app::*;
41/// use std::time::Duration;
42///
43/// # fn func() -> Result<(), failure::Error> {
44/// let request = r#"{
45///         ping
46///     }"#;
47///
48/// let result = query(&ServiceConfig::new_from_path("radio-service", "/home/kubos/config.toml".to_owned())?, request, Some(Duration::from_secs(1)))?;
49///
50/// let data = result.get("ping").unwrap().as_str();
51///
52/// assert_eq!(data, Some("pong"));
53/// # Ok(())
54/// # }
55/// ```
56///
57/// ```
58/// # use failure;
59/// use kubos_app::*;
60/// use std::time::Duration;
61///
62/// # fn func() -> Result<(), failure::Error> {
63/// let request = r#"{
64///         power
65///     }"#;
66///
67/// let result = query(&ServiceConfig::new("antenna-service")?, request, Some(Duration::from_secs(1)))?;
68///
69/// let data = result.get("power").unwrap().as_str();
70///
71/// assert_eq!(data, Some("ON"));
72/// # Ok(())
73/// # }
74/// ```
75///
76pub fn query(
77    config: &ServiceConfig,
78    query: &str,
79    timeout: Option<Duration>,
80) -> AppResult<serde_json::Value> {
81    let client = match timeout {
82        Some(time) => reqwest::Client::builder().timeout(time).build()?,
83        None => reqwest::Client::builder().build()?,
84    };
85
86    let uri = format!(
87        "http://{}",
88        config
89            .hosturl()
90            .ok_or_else(|| format_err!("Unable to fetch addr for service"))?
91    );
92
93    let mut map = ::std::collections::HashMap::new();
94    map.insert("query", query);
95
96    let response: serde_json::Value = client.post(&uri).json(&map).send()?.json()?;
97
98    if let Some(errs) = response.get("errors") {
99        if errs.is_string() {
100            let errs_str = errs.as_str().unwrap();
101            if !errs_str.is_empty() {
102                return Err(format_err!("{}", errs_str.to_string()));
103            }
104        } else if !errs.is_null() {
105            match errs.get("message") {
106                Some(message) => {
107                    return Err(format_err!("{}", message.as_str().unwrap().to_string()));
108                }
109                None => {
110                    return Err(format_err!("{}", serde_json::to_string(errs).unwrap()));
111                }
112            }
113        }
114    }
115
116    match response.get(0) {
117        Some(err) if err.get("message").is_some() => {
118            return Err(format_err!(
119                "{}",
120                err["message"].as_str().unwrap().to_string(),
121            ));
122        }
123        _ => {}
124    }
125
126    match response.get("data") {
127        Some(result) => Ok(result.clone()),
128        None => Err(format_err!(
129            "No result returned in 'data' key: {}",
130            serde_json::to_string(&response).unwrap()
131        )),
132    }
133}