app_bridge 3.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -0
- data/.tool-versions +1 -1
- data/Cargo.lock +292 -751
- data/Cargo.toml +1 -1
- data/README.md +274 -2
- data/ext/app_bridge/Cargo.toml +8 -4
- data/ext/app_bridge/src/app_state.rs +32 -13
- data/ext/app_bridge/src/component.rs +346 -45
- data/ext/app_bridge/src/error_mapping.rs +40 -24
- data/ext/app_bridge/src/file_ops.rs +326 -0
- data/ext/app_bridge/src/lib.rs +21 -3
- data/ext/app_bridge/src/request_builder.rs +343 -152
- data/ext/app_bridge/src/types.rs +95 -0
- data/ext/app_bridge/src/wrappers/action_context.rs +70 -9
- data/ext/app_bridge/src/wrappers/action_response.rs +7 -3
- data/ext/app_bridge/src/wrappers/app.rs +112 -148
- data/ext/app_bridge/src/wrappers/connection.rs +4 -4
- data/ext/app_bridge/src/wrappers/trigger_context.rs +7 -7
- data/ext/app_bridge/src/wrappers/trigger_event.rs +4 -4
- data/ext/app_bridge/src/wrappers/trigger_response.rs +29 -28
- data/ext/app_bridge/wit/{world.wit → v3/world.wit} +3 -0
- data/ext/app_bridge/wit/v4/world.wit +328 -0
- data/ext/app_bridge/wit/v4_1/world.wit +343 -0
- data/lib/app_bridge/app.rb +21 -2
- data/lib/app_bridge/file_processor.rb +131 -0
- data/lib/app_bridge/version.rb +1 -1
- data/lib/app_bridge.rb +25 -0
- data/tasks/fixtures.rake +16 -2
- metadata +9 -4
|
@@ -1,178 +1,338 @@
|
|
|
1
1
|
use crate::app_state::AppState;
|
|
2
|
-
use crate::component::
|
|
3
|
-
|
|
4
|
-
};
|
|
2
|
+
use crate::component::{v3, v4, v4_1};
|
|
3
|
+
use crate::component::v4::standout::app::http::{Method, Request, RequestError, Response};
|
|
5
4
|
use reqwest::Method as ReqwestMethod;
|
|
6
5
|
use std::result::Result::Ok;
|
|
7
6
|
use wasmtime::component::Resource;
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Macro to implement HostRequestBuilder for any version
|
|
10
|
+
//
|
|
11
|
+
// When adding a new version, just add:
|
|
12
|
+
// impl_host_request_builder!(v5);
|
|
13
|
+
// impl_http_type_conversions!(v5);
|
|
14
|
+
// ============================================================================
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
self.next_request_id += 1;
|
|
27
|
-
self.request_list.insert(new_id, request);
|
|
28
|
-
Resource::new_own(new_id)
|
|
29
|
-
}
|
|
16
|
+
macro_rules! impl_host_request_builder {
|
|
17
|
+
($v:ident, $has_body_bytes:ident) => {
|
|
18
|
+
impl $v::standout::app::http::HostRequestBuilder for AppState {
|
|
19
|
+
fn new(&mut self) -> Resource<$v::standout::app::http::RequestBuilder> {
|
|
20
|
+
let id = self.next_request_id;
|
|
21
|
+
self.next_request_id += 1;
|
|
22
|
+
self.request_list.insert(id, Request::default());
|
|
23
|
+
Resource::new_own(id)
|
|
24
|
+
}
|
|
30
25
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
26
|
+
fn method(
|
|
27
|
+
&mut self,
|
|
28
|
+
self_: Resource<$v::standout::app::http::RequestBuilder>,
|
|
29
|
+
method: $v::standout::app::http::Method,
|
|
30
|
+
) -> Resource<$v::standout::app::http::RequestBuilder> {
|
|
31
|
+
let id = self_.rep();
|
|
32
|
+
if let Some(mut request) = self.request_list.get(&id).cloned() {
|
|
33
|
+
request.method = method.into();
|
|
34
|
+
let new_id = self.next_request_id;
|
|
35
|
+
self.next_request_id += 1;
|
|
36
|
+
self.request_list.insert(new_id, request);
|
|
37
|
+
if let Some(bytes) = self.request_body_bytes.get(&id).cloned() {
|
|
38
|
+
self.request_body_bytes.insert(new_id, bytes);
|
|
39
|
+
}
|
|
40
|
+
Resource::new_own(new_id)
|
|
41
|
+
} else {
|
|
42
|
+
Resource::new_own(id)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
46
|
+
fn url(
|
|
47
|
+
&mut self,
|
|
48
|
+
self_: Resource<$v::standout::app::http::RequestBuilder>,
|
|
49
|
+
url: String,
|
|
50
|
+
) -> Resource<$v::standout::app::http::RequestBuilder> {
|
|
51
|
+
let id = self_.rep();
|
|
52
|
+
if let Some(mut request) = self.request_list.get(&id).cloned() {
|
|
53
|
+
request.url = url;
|
|
54
|
+
let new_id = self.next_request_id;
|
|
55
|
+
self.next_request_id += 1;
|
|
56
|
+
self.request_list.insert(new_id, request);
|
|
57
|
+
if let Some(bytes) = self.request_body_bytes.get(&id).cloned() {
|
|
58
|
+
self.request_body_bytes.insert(new_id, bytes);
|
|
59
|
+
}
|
|
60
|
+
Resource::new_own(new_id)
|
|
61
|
+
} else {
|
|
62
|
+
Resource::new_own(id)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fn header(
|
|
67
|
+
&mut self,
|
|
68
|
+
self_: Resource<$v::standout::app::http::RequestBuilder>,
|
|
69
|
+
key: String,
|
|
70
|
+
value: String,
|
|
71
|
+
) -> Resource<$v::standout::app::http::RequestBuilder> {
|
|
72
|
+
let id = self_.rep();
|
|
73
|
+
let mut request = self.request_list.get(&id).cloned().unwrap_or_default();
|
|
74
|
+
request.headers.push((key, value));
|
|
75
|
+
let new_id = self.next_request_id;
|
|
76
|
+
self.next_request_id += 1;
|
|
77
|
+
self.request_list.insert(new_id, request);
|
|
78
|
+
if let Some(bytes) = self.request_body_bytes.get(&id).cloned() {
|
|
79
|
+
self.request_body_bytes.insert(new_id, bytes);
|
|
80
|
+
}
|
|
81
|
+
Resource::new_own(new_id)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fn headers(
|
|
85
|
+
&mut self,
|
|
86
|
+
self_: Resource<$v::standout::app::http::RequestBuilder>,
|
|
87
|
+
headers: Vec<(String, String)>,
|
|
88
|
+
) -> Resource<$v::standout::app::http::RequestBuilder> {
|
|
89
|
+
let id = self_.rep();
|
|
90
|
+
let mut request = self.request_list.get(&id).cloned().unwrap_or_default();
|
|
91
|
+
request.headers.extend(headers);
|
|
92
|
+
let new_id = self.next_request_id;
|
|
93
|
+
self.next_request_id += 1;
|
|
94
|
+
self.request_list.insert(new_id, request);
|
|
95
|
+
if let Some(bytes) = self.request_body_bytes.get(&id).cloned() {
|
|
96
|
+
self.request_body_bytes.insert(new_id, bytes);
|
|
97
|
+
}
|
|
98
|
+
Resource::new_own(new_id)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
fn body(
|
|
102
|
+
&mut self,
|
|
103
|
+
self_: Resource<$v::standout::app::http::RequestBuilder>,
|
|
104
|
+
body: String,
|
|
105
|
+
) -> Resource<$v::standout::app::http::RequestBuilder> {
|
|
106
|
+
let id = self_.rep();
|
|
107
|
+
let mut request = self.request_list.get(&id).cloned().unwrap_or_default();
|
|
108
|
+
request.body = body;
|
|
109
|
+
let new_id = self.next_request_id;
|
|
110
|
+
self.next_request_id += 1;
|
|
111
|
+
self.request_list.insert(new_id, request);
|
|
112
|
+
self.request_body_bytes.remove(&new_id);
|
|
113
|
+
Resource::new_own(new_id)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fn send(
|
|
117
|
+
&mut self,
|
|
118
|
+
self_: Resource<$v::standout::app::http::RequestBuilder>,
|
|
119
|
+
) -> Result<$v::standout::app::http::Response, $v::standout::app::http::RequestError> {
|
|
120
|
+
let id = self_.rep();
|
|
121
|
+
match self.request_list.get(&id).cloned() {
|
|
122
|
+
Some(request) => {
|
|
123
|
+
let body_bytes = self.request_body_bytes.get(&id).map(|b| b.as_slice());
|
|
124
|
+
send_request(&self.client, &request, body_bytes)
|
|
125
|
+
.map(Into::into)
|
|
126
|
+
.map_err(Into::into)
|
|
127
|
+
}
|
|
128
|
+
None => Err($v::standout::app::http::RequestError::Other(
|
|
129
|
+
"Request not found".to_string(),
|
|
130
|
+
)),
|
|
131
|
+
}
|
|
132
|
+
}
|
|
60
133
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
134
|
+
fn drop(
|
|
135
|
+
&mut self,
|
|
136
|
+
rep: Resource<$v::standout::app::http::RequestBuilder>,
|
|
137
|
+
) -> wasmtime::Result<()> {
|
|
138
|
+
self.request_list.remove(&rep.rep());
|
|
139
|
+
self.request_body_bytes.remove(&rep.rep());
|
|
140
|
+
Ok(())
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
fn object(
|
|
144
|
+
&mut self,
|
|
145
|
+
self_: Resource<$v::standout::app::http::RequestBuilder>,
|
|
146
|
+
) -> $v::standout::app::http::Request {
|
|
147
|
+
self.request_list
|
|
148
|
+
.get(&self_.rep())
|
|
149
|
+
.cloned()
|
|
150
|
+
.unwrap_or_default()
|
|
151
|
+
.into()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
impl_host_request_builder_body_bytes!($v, $has_body_bytes);
|
|
73
155
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
macro_rules! impl_host_request_builder_body_bytes {
|
|
160
|
+
($v:ident, yes) => {
|
|
161
|
+
fn body_bytes(
|
|
162
|
+
&mut self,
|
|
163
|
+
self_: Resource<$v::standout::app::http::RequestBuilder>,
|
|
164
|
+
body: Vec<u8>,
|
|
165
|
+
) -> Resource<$v::standout::app::http::RequestBuilder> {
|
|
166
|
+
let id = self_.rep();
|
|
167
|
+
let mut request = self.request_list.get(&id).cloned().unwrap_or_default();
|
|
168
|
+
request.body.clear();
|
|
169
|
+
let new_id = self.next_request_id;
|
|
170
|
+
self.next_request_id += 1;
|
|
171
|
+
self.request_list.insert(new_id, request);
|
|
172
|
+
self.request_body_bytes.insert(new_id, body);
|
|
173
|
+
Resource::new_own(new_id)
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
($v:ident, no) => {};
|
|
177
|
+
}
|
|
79
178
|
|
|
80
|
-
#[doc = " Add a body to the request"]
|
|
81
|
-
fn body(
|
|
82
|
-
&mut self,
|
|
83
|
-
self_: Resource<RequestBuilder>,
|
|
84
|
-
body: wasmtime::component::__internal::String,
|
|
85
|
-
) -> Resource<RequestBuilder> {
|
|
86
|
-
let id = self_.rep();
|
|
87
|
-
let mut request = self.request_list.get(&id).cloned().unwrap_or_default();
|
|
88
|
-
request.body = body;
|
|
89
|
-
let new_id = self.next_request_id;
|
|
90
|
-
self.next_request_id += 1;
|
|
91
|
-
self.request_list.insert(new_id, request);
|
|
92
|
-
Resource::new_own(new_id)
|
|
93
|
-
}
|
|
94
179
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
180
|
+
// ============================================================================
|
|
181
|
+
// Macro to implement HTTP type conversions for a version
|
|
182
|
+
// ============================================================================
|
|
183
|
+
|
|
184
|
+
macro_rules! impl_http_type_conversions {
|
|
185
|
+
($v:ident) => {
|
|
186
|
+
impl From<$v::standout::app::http::Method> for Method {
|
|
187
|
+
fn from(m: $v::standout::app::http::Method) -> Self {
|
|
188
|
+
use $v::standout::app::http::Method as V;
|
|
189
|
+
match m {
|
|
190
|
+
V::Get => Self::Get,
|
|
191
|
+
V::Post => Self::Post,
|
|
192
|
+
V::Put => Self::Put,
|
|
193
|
+
V::Delete => Self::Delete,
|
|
194
|
+
V::Patch => Self::Patch,
|
|
195
|
+
V::Options => Self::Options,
|
|
196
|
+
V::Head => Self::Head,
|
|
197
|
+
}
|
|
198
|
+
}
|
|
106
199
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
value.to_str().unwrap_or_default().to_string(),
|
|
119
|
-
));
|
|
200
|
+
|
|
201
|
+
impl From<Method> for $v::standout::app::http::Method {
|
|
202
|
+
fn from(m: Method) -> Self {
|
|
203
|
+
match m {
|
|
204
|
+
Method::Get => Self::Get,
|
|
205
|
+
Method::Post => Self::Post,
|
|
206
|
+
Method::Put => Self::Put,
|
|
207
|
+
Method::Delete => Self::Delete,
|
|
208
|
+
Method::Patch => Self::Patch,
|
|
209
|
+
Method::Options => Self::Options,
|
|
210
|
+
Method::Head => Self::Head,
|
|
120
211
|
}
|
|
121
|
-
response.body = resp.text().unwrap_or_default();
|
|
122
|
-
Ok(response)
|
|
123
212
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
impl From<Response> for $v::standout::app::http::Response {
|
|
216
|
+
fn from(r: Response) -> Self {
|
|
217
|
+
Self {
|
|
218
|
+
status: r.status,
|
|
219
|
+
headers: r.headers,
|
|
220
|
+
body: r.body,
|
|
221
|
+
}
|
|
133
222
|
}
|
|
134
223
|
}
|
|
135
|
-
}
|
|
136
224
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
225
|
+
impl From<Request> for $v::standout::app::http::Request {
|
|
226
|
+
fn from(r: Request) -> Self {
|
|
227
|
+
Self {
|
|
228
|
+
method: r.method.into(),
|
|
229
|
+
url: r.url,
|
|
230
|
+
headers: r.headers,
|
|
231
|
+
body: r.body,
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
impl From<RequestError> for $v::standout::app::http::RequestError {
|
|
237
|
+
fn from(e: RequestError) -> Self {
|
|
238
|
+
match e {
|
|
239
|
+
RequestError::Other(msg) => Self::Other(msg),
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ============================================================================
|
|
247
|
+
// Generate implementations for all supported versions
|
|
248
|
+
// When adding v5, just add:
|
|
249
|
+
// impl_host_request_builder!(v5);
|
|
250
|
+
// impl_http_type_conversions!(v5);
|
|
251
|
+
// ============================================================================
|
|
252
|
+
|
|
253
|
+
impl_host_request_builder!(v3, no);
|
|
254
|
+
impl_host_request_builder!(v4, no);
|
|
255
|
+
impl_host_request_builder!(v4_1, yes);
|
|
256
|
+
|
|
257
|
+
impl_http_type_conversions!(v3);
|
|
258
|
+
// Note: v4 doesn't need conversions since we use v4 types as the canonical internal types
|
|
259
|
+
impl_http_type_conversions!(v4_1);
|
|
260
|
+
|
|
261
|
+
// ============================================================================
|
|
262
|
+
// Shared request sending logic
|
|
263
|
+
// ============================================================================
|
|
264
|
+
|
|
265
|
+
fn send_request(
|
|
266
|
+
client: &std::sync::Arc<std::sync::Mutex<reqwest::blocking::Client>>,
|
|
267
|
+
request: &Request,
|
|
268
|
+
body_bytes: Option<&[u8]>,
|
|
269
|
+
) -> Result<Response, RequestError> {
|
|
270
|
+
let client = client.lock().unwrap();
|
|
271
|
+
let mut builder = client.request(request.method.clone().into(), &request.url);
|
|
272
|
+
|
|
273
|
+
for (key, value) in &request.headers {
|
|
274
|
+
builder = builder.header(key, value);
|
|
141
275
|
}
|
|
276
|
+
builder = if let Some(bytes) = body_bytes {
|
|
277
|
+
builder.body(bytes.to_vec())
|
|
278
|
+
} else {
|
|
279
|
+
builder.body(request.body.clone())
|
|
280
|
+
};
|
|
142
281
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
282
|
+
match builder.send() {
|
|
283
|
+
Ok(resp) => {
|
|
284
|
+
let headers = resp
|
|
285
|
+
.headers()
|
|
286
|
+
.iter()
|
|
287
|
+
.map(|(k, v)| {
|
|
288
|
+
(
|
|
289
|
+
k.as_str().to_string(),
|
|
290
|
+
v.to_str().unwrap_or_default().to_string(),
|
|
291
|
+
)
|
|
292
|
+
})
|
|
293
|
+
.collect();
|
|
294
|
+
|
|
295
|
+
Ok(Response {
|
|
296
|
+
status: resp.status().as_u16(),
|
|
297
|
+
headers,
|
|
298
|
+
body: resp.text().unwrap_or_default(),
|
|
299
|
+
})
|
|
300
|
+
}
|
|
301
|
+
Err(error) => Err(RequestError::Other(format!(
|
|
302
|
+
"Request failed to {} {}: {}",
|
|
303
|
+
request.method, request.url, error
|
|
304
|
+
))),
|
|
146
305
|
}
|
|
147
306
|
}
|
|
148
307
|
|
|
308
|
+
// ============================================================================
|
|
309
|
+
// Standard type implementations (used by all versions)
|
|
310
|
+
// ============================================================================
|
|
311
|
+
|
|
149
312
|
impl From<Method> for ReqwestMethod {
|
|
150
313
|
fn from(method: Method) -> Self {
|
|
151
314
|
match method {
|
|
152
|
-
Method::Get =>
|
|
153
|
-
Method::Post =>
|
|
154
|
-
Method::Put =>
|
|
155
|
-
Method::Delete =>
|
|
156
|
-
Method::Patch =>
|
|
157
|
-
Method::Head =>
|
|
158
|
-
Method::Options =>
|
|
315
|
+
Method::Get => Self::GET,
|
|
316
|
+
Method::Post => Self::POST,
|
|
317
|
+
Method::Put => Self::PUT,
|
|
318
|
+
Method::Delete => Self::DELETE,
|
|
319
|
+
Method::Patch => Self::PATCH,
|
|
320
|
+
Method::Head => Self::HEAD,
|
|
321
|
+
Method::Options => Self::OPTIONS,
|
|
159
322
|
}
|
|
160
323
|
}
|
|
161
324
|
}
|
|
162
325
|
|
|
163
326
|
impl Default for Request {
|
|
164
327
|
fn default() -> Self {
|
|
165
|
-
let version = env!("CARGO_PKG_VERSION");
|
|
166
|
-
let user_agent = format!("Standout-AppBridge/{version}");
|
|
167
|
-
let headers = vec![
|
|
168
|
-
("User-Agent".to_string(), user_agent.into()),
|
|
169
|
-
];
|
|
170
|
-
|
|
171
328
|
Self {
|
|
172
|
-
url:
|
|
329
|
+
url: String::new(),
|
|
173
330
|
method: Method::Get,
|
|
174
|
-
body:
|
|
175
|
-
headers
|
|
331
|
+
body: String::new(),
|
|
332
|
+
headers: vec![(
|
|
333
|
+
"User-Agent".to_string(),
|
|
334
|
+
format!("Standout-AppBridge/{}", env!("CARGO_PKG_VERSION")),
|
|
335
|
+
)],
|
|
176
336
|
}
|
|
177
337
|
}
|
|
178
338
|
}
|
|
@@ -182,33 +342,38 @@ impl Default for Response {
|
|
|
182
342
|
Self {
|
|
183
343
|
status: 0,
|
|
184
344
|
headers: Vec::new(),
|
|
185
|
-
body:
|
|
345
|
+
body: String::new(),
|
|
186
346
|
}
|
|
187
347
|
}
|
|
188
348
|
}
|
|
189
349
|
|
|
190
350
|
impl std::fmt::Display for Method {
|
|
191
351
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
192
|
-
match self {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
352
|
+
f.write_str(match self {
|
|
353
|
+
Self::Get => "GET",
|
|
354
|
+
Self::Post => "POST",
|
|
355
|
+
Self::Put => "PUT",
|
|
356
|
+
Self::Delete => "DELETE",
|
|
357
|
+
Self::Patch => "PATCH",
|
|
358
|
+
Self::Head => "HEAD",
|
|
359
|
+
Self::Options => "OPTIONS",
|
|
360
|
+
})
|
|
201
361
|
}
|
|
202
362
|
}
|
|
203
363
|
|
|
364
|
+
// ============================================================================
|
|
365
|
+
// Tests
|
|
366
|
+
// ============================================================================
|
|
204
367
|
|
|
205
368
|
#[cfg(test)]
|
|
206
369
|
mod tests {
|
|
207
370
|
use super::*;
|
|
208
|
-
use httpmock::{
|
|
371
|
+
use httpmock::{Method::GET, Method::POST, MockServer};
|
|
209
372
|
|
|
210
373
|
#[test]
|
|
211
374
|
fn sends_request_with_default_user_agent() {
|
|
375
|
+
use v4::standout::app::http::HostRequestBuilder;
|
|
376
|
+
|
|
212
377
|
let version = env!("CARGO_PKG_VERSION");
|
|
213
378
|
let user_agent = format!("Standout-AppBridge/{version}");
|
|
214
379
|
|
|
@@ -231,4 +396,30 @@ mod tests {
|
|
|
231
396
|
assert_eq!(response.status, 200);
|
|
232
397
|
mock.assert();
|
|
233
398
|
}
|
|
399
|
+
|
|
400
|
+
#[test]
|
|
401
|
+
fn sends_binary_body_when_bytes_present() {
|
|
402
|
+
use v4_1::standout::app::http::HostRequestBuilder;
|
|
403
|
+
|
|
404
|
+
let server = MockServer::start();
|
|
405
|
+
let mock = server.mock(|when, then| {
|
|
406
|
+
when.method(POST)
|
|
407
|
+
.path("/upload")
|
|
408
|
+
.body("raw-bytes");
|
|
409
|
+
then.status(200);
|
|
410
|
+
});
|
|
411
|
+
let url = format!("{}/upload", server.base_url());
|
|
412
|
+
|
|
413
|
+
let mut app_state = AppState::default();
|
|
414
|
+
let builder = app_state.new();
|
|
415
|
+
let builder = app_state.method(builder, v4_1::standout::app::http::Method::Post);
|
|
416
|
+
let builder = app_state.url(builder, url);
|
|
417
|
+
let builder = app_state.body(builder, "string-body".to_string());
|
|
418
|
+
let builder = app_state.body_bytes(builder, b"raw-bytes".to_vec());
|
|
419
|
+
|
|
420
|
+
let response = app_state.send(builder).expect("Request failed");
|
|
421
|
+
|
|
422
|
+
assert_eq!(response.status, 200);
|
|
423
|
+
mock.assert();
|
|
424
|
+
}
|
|
234
425
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
//! Canonical types used internally by the bridge.
|
|
2
|
+
//!
|
|
3
|
+
//! These are version-agnostic and converted to/from versioned WIT types at the boundary.
|
|
4
|
+
|
|
5
|
+
use std::fmt;
|
|
6
|
+
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Canonical Types - used throughout the codebase, not tied to any version
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
#[derive(Debug, Clone)]
|
|
12
|
+
pub struct AppError {
|
|
13
|
+
pub code: ErrorCode,
|
|
14
|
+
pub message: String,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
impl AppError {
|
|
18
|
+
pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
|
|
19
|
+
Self {
|
|
20
|
+
code,
|
|
21
|
+
message: message.into(),
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
impl fmt::Display for AppError {
|
|
27
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
28
|
+
write!(f, "{:?}: {}", self.code, self.message)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
impl std::error::Error for AppError {}
|
|
33
|
+
|
|
34
|
+
#[derive(Debug, Clone)]
|
|
35
|
+
pub enum ErrorCode {
|
|
36
|
+
Unauthenticated,
|
|
37
|
+
Forbidden,
|
|
38
|
+
Misconfigured,
|
|
39
|
+
Unsupported,
|
|
40
|
+
RateLimit,
|
|
41
|
+
Timeout,
|
|
42
|
+
Unavailable,
|
|
43
|
+
InternalError,
|
|
44
|
+
MalformedResponse,
|
|
45
|
+
Other,
|
|
46
|
+
RetryWithReference(ReferenceObject),
|
|
47
|
+
CompleteWorkflow,
|
|
48
|
+
CompleteParent,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#[derive(Debug, Clone)]
|
|
52
|
+
pub struct ReferenceObject {
|
|
53
|
+
pub reference: String,
|
|
54
|
+
pub status: String,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#[derive(Debug, Clone)]
|
|
58
|
+
pub struct Connection {
|
|
59
|
+
pub id: String,
|
|
60
|
+
pub name: String,
|
|
61
|
+
pub serialized_data: String,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#[derive(Debug, Clone)]
|
|
65
|
+
pub struct TriggerContext {
|
|
66
|
+
pub trigger_id: String,
|
|
67
|
+
pub connection: Connection,
|
|
68
|
+
pub store: String,
|
|
69
|
+
pub serialized_input: String,
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
#[derive(Debug, Clone)]
|
|
73
|
+
pub struct ActionContext {
|
|
74
|
+
pub action_id: String,
|
|
75
|
+
pub connection: Connection,
|
|
76
|
+
pub serialized_input: String,
|
|
77
|
+
pub reference_object: Option<ReferenceObject>,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
#[derive(Debug, Clone)]
|
|
81
|
+
pub struct TriggerEvent {
|
|
82
|
+
pub id: String,
|
|
83
|
+
pub serialized_data: String,
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#[derive(Debug, Clone)]
|
|
87
|
+
pub struct TriggerResponse {
|
|
88
|
+
pub store: String,
|
|
89
|
+
pub events: Vec<TriggerEvent>,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
#[derive(Debug, Clone)]
|
|
93
|
+
pub struct ActionResponse {
|
|
94
|
+
pub serialized_output: String,
|
|
95
|
+
}
|