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
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
use std::collections::BTreeMap;
use log::error;
use serde::{Deserialize, Serialize};
use crate::spec::{
spec_extensions, Callback, Error, ExternalDoc, ObjectOrReference, Parameter, RequestBody,
Response, SecurityRequirement, Server, Spec,
};
/// Describes a single API operation on a path.
///
/// See <https://spec.openapis.org/oas/v3.1.1#operation-object>.
#[derive(Debug, Clone, Default, PartialEq, Deserialize, Serialize)]
pub struct Operation {
/// A list of tags for API documentation control.
///
/// Tags can be used for logical grouping of operations by resources or any other qualifier.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
/// A short summary of what the operation does.
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
/// A verbose explanation of the operation behavior.
///
/// [CommonMark syntax](https://spec.commonmark.org) MAY be used for rich text representation.
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
/// Additional external documentation for this operation.
#[serde(rename = "externalDocs", skip_serializing_if = "Option::is_none")]
pub external_docs: Option<ExternalDoc>,
/// String used to uniquely identify the operation within this spec.
///
/// The ID MUST be unique among all operations described in the API. Tools and libraries MAY use
/// the operation ID to uniquely identify an operation, therefore, it is RECOMMENDED to follow
/// common programming naming conventions.
#[serde(rename = "operationId", skip_serializing_if = "Option::is_none")]
pub operation_id: Option<String>,
/// A list of parameters that are applicable for this operation.
///
/// If a parameter is already defined at the [Path Item], the new definition will override it
/// but can never remove it. The list MUST NOT include duplicated parameters. A unique parameter
/// is defined by a combination of a [name] and [location] The list can use the
/// [Reference Object] to link to parameters that are defined at the
/// [OpenAPI Object's components/parameters].
///
/// [Path Item]: https://spec.openapis.org/oas/v3.1.1#pathItemParameters
/// [name]: https://spec.openapis.org/oas/v3.1.1#parameterName
/// [location]: https://spec.openapis.org/oas/v3.1.1#parameterIn
/// [Reference Object]: https://spec.openapis.org/oas/v3.1.1#reference-object
/// [OpenAPI Object's components/parameters]: https://spec.openapis.org/oas/v3.1.1#components-parameters
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub parameters: Vec<ObjectOrReference<Parameter>>,
/// The request body applicable for this operation.
///
/// The `requestBody` is only supported in HTTP methods where the HTTP/1.1 specification RFC
/// 7231 has explicitly defined semantics for request bodies. In other cases where the HTTP spec
/// is vague, `requestBody` SHALL be ignored by consumers.
#[serde(rename = "requestBody", skip_serializing_if = "Option::is_none")]
pub request_body: Option<ObjectOrReference<RequestBody>>,
/// The list of possible responses as they are returned from executing this operation.
///
/// A container for the expected responses of an operation. The container maps a HTTP response
/// code to the expected response.
///
/// The documentation is not necessarily expected to cover all possible HTTP response codes
/// because they may not be known in advance. However, documentation is expected to cover
/// a successful operation response and any known errors.
///
/// The `default` MAY be used as a default response object for all HTTP codes that are not
/// covered individually by the specification.
///
/// The `Responses Object` MUST contain at least one response code, and it SHOULD be the
/// response for a successful operation call.
///
/// See <https://spec.openapis.org/oas/v3.1.1#responses-object>.
pub responses: Option<BTreeMap<String, ObjectOrReference<Response>>>,
/// A map of possible out-of band callbacks related to the parent operation.
///
/// The key is a unique identifier for the [Callback Object]. Each value in the map is a
/// [Callback Object] that describes a request that may be initiated by the API provider and the
/// expected responses.
///
/// [Callback Object]: https://spec.openapis.org/oas/v3.1.1#callback-object
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub callbacks: BTreeMap<String, Callback>,
/// Declares this operation to be deprecated.
///
/// Consumers SHOULD refrain from usage of the declared operation. Default value is `false`.
#[serde(skip_serializing_if = "Option::is_none")]
pub deprecated: Option<bool>,
/// A declaration of which security mechanisms can be used for this operation.
///
/// The list of values includes alternative Security Requirement Objects that can be used. Only
/// one of the Security Requirement Objects need to be satisfied to authorize a request. To make
/// security optional, an empty security requirement ({}) can be included in the array. This
/// definition overrides any declared top-level security. To remove a top-level security
/// declaration, an empty array can be used.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub security: Vec<SecurityRequirement>,
/// An alternative `server` array to service this operation.
///
/// If an alternative `server` object is specified at the Path Item Object or Root level, it
/// will be overridden by this value.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub servers: Vec<Server>,
/// Specification extensions.
///
/// Only "x-" prefixed keys are collected, and the prefix is stripped.
///
/// See <https://spec.openapis.org/oas/v3.1.1#specification-extensions>.
#[serde(flatten, with = "spec_extensions")]
pub extensions: BTreeMap<String, serde_json::Value>,
}
impl Operation {
/// Resolves and returns this operation's request body.
pub fn request_body(&self, spec: &Spec) -> Result<Option<RequestBody>, Error> {
let Some(req_body) = self.request_body.as_ref() else {
return Ok(None);
};
let req_body = req_body.resolve(spec).map_err(Error::Ref)?;
Ok(Some(req_body))
}
/// Resolves and returns map of this operation's responses, keyed by status code.
pub fn responses(&self, spec: &Spec) -> BTreeMap<String, Response> {
self.responses
.iter()
.flatten()
.filter_map(|(name, oor)| {
oor.resolve(spec)
.map(|obj| (name.clone(), obj))
// TODO: find better error solution
.map_err(|err| error!("{err}"))
.ok()
})
.collect()
}
/// Resolves and returns list of this operation's parameters.
pub fn parameters(&self, spec: &Spec) -> Result<Vec<Parameter>, Error> {
let params = self
.parameters
.iter()
// TODO: find better error solution, maybe vec<result<_>>
.filter_map(|oor| oor.resolve(spec).map_err(|err| error!("{err}")).ok())
.collect();
Ok(params)
}
/// Finds, resolves, and returns one of this operation's parameters by name.
pub fn parameter(&self, search: &str, spec: &Spec) -> Result<Option<Parameter>, Error> {
let param = self
.parameters(spec)?
.iter()
.find(|param| param.name == search)
.cloned();
Ok(param)
}
}