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
use crate::context::with_puff_context;
use crate::errors::to_py_error;
use crate::graphql::{
convert_pyany_to_input, juniper_value_to_python, AggroContext, PuffGraphqlRoot,
};
use crate::prelude::ToText;
use crate::python::async_python::run_python_async;
use crate::python::postgres::Connection;
use juniper::execute;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList, PyString};
use pyo3::{PyObject, PyResult, Python};
use std::collections::HashMap;
#[pyclass]
#[derive(Clone)]
pub struct GlobalGraphQL;
impl ToPyObject for GlobalGraphQL {
fn to_object(&self, py: Python<'_>) -> PyObject {
self.clone().into_py(py)
}
}
#[pymethods]
impl GlobalGraphQL {
fn __call__(&self, py: Python) -> PyObject {
with_puff_context(|ctx| PythonGraphql(ctx.gql())).to_object(py)
}
}
#[pyclass]
#[derive(Clone)]
pub struct PythonGraphql(PuffGraphqlRoot);
impl ToPyObject for PythonGraphql {
fn to_object(&self, py: Python<'_>) -> PyObject {
self.clone().into_py(py)
}
}
#[pymethods]
impl PythonGraphql {
pub fn query(
&self,
return_fun: PyObject,
query: String,
variables: &PyDict,
conn: Option<&Connection>,
auth_token: Option<&PyString>,
) -> PyResult<()> {
let bearer = auth_token.map(|t| t.to_text());
let mut hm = HashMap::with_capacity(variables.len());
for (k, v) in variables {
let variables = to_py_error("GQL Inputs", convert_pyany_to_input(v))?;
hm.insert(k.to_string(), variables);
}
let this_root = self.0.clone();
let this_conn = conn.map(|f| f.clone()).unwrap_or_else(|| {
let pool = with_puff_context(|ctx| ctx.postgres().pool());
Connection::new(pool)
});
run_python_async(return_fun, async move {
let (value, errors) = execute(
query.as_str(),
None,
&this_root,
&hm,
&AggroContext::new_with_connection(bearer, this_conn),
)
.await?;
Python::with_gil(|py| {
let pydict = PyDict::new(py);
let data = juniper_value_to_python(py, &value)?;
if !errors.is_empty() {
let py_errors = PyList::empty(py);
for error in errors {
let pydict = PyDict::new(py);
pydict.set_item("path", error.path())?;
pydict.set_item("error", format!("{:?}", error.error()))?;
pydict.set_item("location", format!("{:?}", error.location()))?;
py_errors.append(pydict)?;
}
pydict.set_item("errors", py_errors)?;
}
pydict.set_item("data", data)?;
let r: PyObject = pydict.into_py(py);
Ok(r)
})
});
Ok(())
}
}