Struct async_graphql::Request

source ·
#[non_exhaustive]
pub struct Request { pub query: String, pub operation_name: Option<String>, pub variables: Variables, pub uploads: Vec<UploadValue>, pub data: Data, pub extensions: HashMap<String, Value>, pub introspection_mode: IntrospectionMode, /* private fields */ }
Expand description

GraphQL request.

This can be deserialized from a structure of the query string, the operation name and the variables. The names are all in camelCase (e.g. operationName).

Fields (Non-exhaustive)§

This struct is marked as non-exhaustive
Non-exhaustive structs could have additional fields added in future. Therefore, non-exhaustive structs cannot be constructed in external crates using the traditional Struct { .. } syntax; cannot be matched against without a wildcard ..; and struct update syntax will not work.
§query: String

The query source of the request.

§operation_name: Option<String>

The operation name of the request.

§variables: Variables

The variables of the request.

§uploads: Vec<UploadValue>

Uploads sent with the request.

§data: Data

The data of the request that can be accessed through Context::data.

This data is only valid for this request

§extensions: HashMap<String, Value>

The extensions config of the request.

§introspection_mode: IntrospectionMode

Sets the introspection mode for this request (defaults to IntrospectionMode::Enabled).

Implementations§

Create a request object with query source.

Examples found in repository?
src/request.rs (line 162)
161
162
163
    fn from(query: T) -> Self {
        Self::new(query)
    }
More examples
Hide additional examples
src/http/mod.rs (line 58)
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
pub fn parse_query_string(input: &str) -> Result<Request, ParseRequestError> {
    #[derive(Deserialize)]
    struct RequestSerde {
        #[serde(default)]
        pub query: String,
        pub operation_name: Option<String>,
        pub variables: Option<String>,
        pub extensions: Option<String>,
    }

    let request: RequestSerde = serde_urlencoded::from_str(input)
        .map_err(|err| std::io::Error::new(ErrorKind::Other, err))?;
    let variables = request
        .variables
        .map(|data| serde_json::from_str(&data))
        .transpose()
        .map_err(|err| {
            std::io::Error::new(ErrorKind::Other, format!("invalid variables: {}", err))
        })?
        .unwrap_or_default();
    let extensions = request
        .extensions
        .map(|data| serde_json::from_str(&data))
        .transpose()
        .map_err(|err| {
            std::io::Error::new(ErrorKind::Other, format!("invalid extensions: {}", err))
        })?
        .unwrap_or_default();

    Ok(Request {
        operation_name: request.operation_name,
        variables,
        extensions,
        ..Request::new(request.query)
    })
}

Specify the operation name of the request.

Specify the variables.

Insert some data for this request.

Disable introspection queries for this request.

Only allow introspection queries for this request.

Performs parsing of query ahead of execution.

This effectively allows to inspect query information, before passing request to schema for execution as long as query is valid.

Set a variable to an upload value.

var_path is a dot-separated path to the item that begins with variables, for example variables.files.2.content is equivalent to the Rust code request.variables["files"][2]["content"]. If no variable exists at the path this function won’t do anything.

Examples found in repository?
src/http/multipart.rs (line 156)
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
pub(super) async fn receive_batch_multipart(
    body: impl AsyncRead + Send,
    boundary: impl Into<String>,
    opts: MultipartOptions,
) -> Result<BatchRequest, ParseRequestError> {
    let mut multipart = Multipart::with_constraints(
        ReaderStream::new(body),
        boundary,
        Constraints::new().size_limit({
            let mut limit = SizeLimit::new();
            if let (Some(max_file_size), Some(max_num_files)) =
                (opts.max_file_size, opts.max_num_files)
            {
                limit = limit.whole_stream((max_file_size * max_num_files) as u64);
            }
            if let Some(max_file_size) = opts.max_file_size {
                limit = limit.per_field(max_file_size as u64);
            }
            limit
        }),
    );

    let mut request = None;
    let mut map = None;
    let mut files = Vec::new();

    while let Some(field) = multipart.next_field().await? {
        // in multipart, each field / file can actually have a own Content-Type.
        // We use this to determine the encoding of the graphql query
        let content_type = field
            .content_type()
            // default to json
            .unwrap_or(&mime::APPLICATION_JSON)
            .clone();
        match field.name() {
            Some("operations") => {
                let body = field.bytes().await?;
                request = Some(
                    super::receive_batch_body_no_multipart(&content_type, body.as_ref()).await?,
                )
            }
            Some("map") => {
                let map_bytes = field.bytes().await?;

                match (content_type.type_(), content_type.subtype()) {
                    // cbor is in application/octet-stream.
                    // TODO: wait for mime to add application/cbor and match against that too
                    // Note: we actually differ here from the inoffical spec for this:
                    // (https://github.com/jaydenseric/graphql-multipart-request-spec#multipart-form-field-structure)
                    // It says: "map: A JSON encoded map of where files occurred in the operations.
                    // For each file, the key is the file multipart form field name and the value is
                    // an array of operations paths." However, I think, that
                    // since we accept CBOR as operation, which is valid, we should also accept it
                    // as the mapping for the files.
                    #[cfg(feature = "cbor")]
                    (mime::OCTET_STREAM, _) | (mime::APPLICATION, mime::OCTET_STREAM) => {
                        map = Some(
                            serde_cbor::from_slice::<HashMap<String, Vec<String>>>(&map_bytes)
                                .map_err(|e| ParseRequestError::InvalidFilesMap(Box::new(e)))?,
                        );
                    }
                    // default to json
                    _ => {
                        map = Some(
                            serde_json::from_slice::<HashMap<String, Vec<String>>>(&map_bytes)
                                .map_err(|e| ParseRequestError::InvalidFilesMap(Box::new(e)))?,
                        );
                    }
                }
            }
            _ => {
                if let Some(name) = field.name().map(ToString::to_string) {
                    if let Some(filename) = field.file_name().map(ToString::to_string) {
                        let content_type = field.content_type().map(ToString::to_string);

                        #[cfg(feature = "tempfile")]
                        let content = {
                            use std::io::{Seek, SeekFrom, Write};

                            let mut field = field;
                            let mut file = tempfile::tempfile().map_err(ParseRequestError::Io)?;
                            while let Some(chunk) = field.chunk().await? {
                                file.write(&chunk).map_err(ParseRequestError::Io)?;
                            }
                            file.seek(SeekFrom::Start(0))?;
                            file
                        };

                        #[cfg(not(feature = "tempfile"))]
                        let content = field.bytes().await?;

                        files.push((name, filename, content_type, content));
                    }
                }
            }
        }
    }

    let mut request: BatchRequest = request.ok_or(ParseRequestError::MissingOperatorsPart)?;
    let map = map.as_mut().ok_or(ParseRequestError::MissingMapPart)?;

    for (name, filename, content_type, file) in files {
        if let Some(var_paths) = map.remove(&name) {
            let upload = UploadValue {
                filename,
                content_type,
                content: file,
            };

            for var_path in var_paths {
                match &mut request {
                    BatchRequest::Single(request) => {
                        request.set_upload(&var_path, upload.try_clone()?);
                    }
                    BatchRequest::Batch(requests) => {
                        let mut s = var_path.splitn(2, '.');
                        let idx = s.next().and_then(|idx| idx.parse::<usize>().ok());
                        let path = s.next();

                        if let (Some(idx), Some(path)) = (idx, path) {
                            if let Some(request) = requests.get_mut(idx) {
                                request.set_upload(path, upload.try_clone()?);
                            }
                        }
                    }
                }
            }
        }
    }

    if !map.is_empty() {
        return Err(ParseRequestError::MissingFiles);
    }

    Ok(request)
}

Trait Implementations§

Formats the value using the given formatter. Read more
Deserialize this value from the given Serde deserializer. Read more
Converts to this type from the input type.
Converts to this type from the input type.
Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Attaches the provided Context to this type, returning a WithContext wrapper. Read more
Attaches the current Context to this type, returning a WithContext wrapper. Read more
Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Should always be Self
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.
Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more