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