Add support for text/plain and text/x-markdown body content types (#593)
This commit is contained in:
parent
6dc2a0ff2d
commit
a1de78dbeb
|
@ -8,8 +8,9 @@ use quote::{format_ident, quote, ToTokens};
|
|||
|
||||
use crate::{
|
||||
method::{
|
||||
HttpMethod, OperationParameter, OperationParameterKind,
|
||||
OperationParameterType, OperationResponse, OperationResponseStatus,
|
||||
BodyContentType, HttpMethod, OperationParameter,
|
||||
OperationParameterKind, OperationParameterType, OperationResponse,
|
||||
OperationResponseStatus,
|
||||
},
|
||||
to_schema::ToSchema,
|
||||
util::{sanitize, Case},
|
||||
|
@ -165,8 +166,18 @@ impl Generator {
|
|||
.get_type(arg_type_id)
|
||||
.unwrap()
|
||||
.parameter_ident(),
|
||||
OperationParameterType::RawBody => quote! {
|
||||
serde_json::Value
|
||||
OperationParameterType::RawBody => match kind {
|
||||
OperationParameterKind::Body(
|
||||
BodyContentType::OctetStream,
|
||||
) => quote! {
|
||||
serde_json::Value
|
||||
},
|
||||
OperationParameterKind::Body(
|
||||
BodyContentType::Text(_),
|
||||
) => quote! {
|
||||
String
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -229,15 +240,25 @@ impl Generator {
|
|||
};
|
||||
}
|
||||
OperationParameterKind::Header(_) => quote! { todo!() },
|
||||
OperationParameterKind::Body(_) => match typ {
|
||||
OperationParameterType::Type(_) => quote! {
|
||||
Self(self.0.json_body_obj(value))
|
||||
OperationParameterKind::Body(body_content_type) => {
|
||||
match typ {
|
||||
OperationParameterType::Type(_) => quote! {
|
||||
Self(self.0.json_body_obj(value))
|
||||
|
||||
},
|
||||
OperationParameterType::RawBody => quote! {
|
||||
Self(self.0.json_body(value))
|
||||
},
|
||||
},
|
||||
},
|
||||
OperationParameterType::RawBody => {
|
||||
match body_content_type {
|
||||
BodyContentType::OctetStream => quote! {
|
||||
Self(self.0.json_body(value))
|
||||
},
|
||||
BodyContentType::Text(_) => quote! {
|
||||
Self(self.0.body(value))
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
pub fn #name_ident(self, value: #arg_type_name) -> Self {
|
||||
|
|
|
@ -137,6 +137,7 @@ pub enum BodyContentType {
|
|||
OctetStream,
|
||||
Json,
|
||||
FormUrlencoded,
|
||||
Text(String),
|
||||
}
|
||||
|
||||
impl FromStr for BodyContentType {
|
||||
|
@ -148,6 +149,9 @@ impl FromStr for BodyContentType {
|
|||
"application/octet-stream" => Ok(Self::OctetStream),
|
||||
"application/json" => Ok(Self::Json),
|
||||
"application/x-www-form-urlencoded" => Ok(Self::FormUrlencoded),
|
||||
"text/plain" | "text/x-markdown" => {
|
||||
Ok(Self::Text(String::from(&s[..offset])))
|
||||
}
|
||||
_ => Err(Error::UnexpectedFormat(format!(
|
||||
"unexpected content type: {}",
|
||||
s
|
||||
|
@ -156,6 +160,19 @@ impl FromStr for BodyContentType {
|
|||
}
|
||||
}
|
||||
|
||||
use std::fmt;
|
||||
|
||||
impl fmt::Display for BodyContentType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
Self::OctetStream => "application/octet-stream",
|
||||
Self::Json => "application/json",
|
||||
Self::FormUrlencoded => "application/x-www-form-urlencoded",
|
||||
Self::Text(typ) => &typ,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct OperationResponse {
|
||||
pub status_code: OperationResponseStatus,
|
||||
|
@ -601,7 +618,19 @@ impl Generator {
|
|||
quote! { Option<#t> }
|
||||
}
|
||||
(OperationParameterType::RawBody, false) => {
|
||||
quote! { B }
|
||||
match ¶m.kind {
|
||||
OperationParameterKind::Body(
|
||||
BodyContentType::OctetStream,
|
||||
) => {
|
||||
quote! { B }
|
||||
}
|
||||
OperationParameterKind::Body(
|
||||
BodyContentType::Text(_),
|
||||
) => {
|
||||
quote! { String }
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
(OperationParameterType::RawBody, true) => unreachable!(),
|
||||
};
|
||||
|
@ -611,10 +640,13 @@ impl Generator {
|
|||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let raw_body_param = method
|
||||
.params
|
||||
.iter()
|
||||
.any(|param| param.typ == OperationParameterType::RawBody);
|
||||
let raw_body_param = method.params.iter().any(|param| {
|
||||
param.typ == OperationParameterType::RawBody
|
||||
&& param.kind
|
||||
== OperationParameterKind::Body(
|
||||
BodyContentType::OctetStream,
|
||||
)
|
||||
});
|
||||
|
||||
let bounds = if raw_body_param {
|
||||
quote! { <'a, B: Into<reqwest::Body> > }
|
||||
|
@ -935,6 +967,18 @@ impl Generator {
|
|||
)
|
||||
.body(body)
|
||||
}),
|
||||
(
|
||||
OperationParameterKind::Body(BodyContentType::Text(mime_type)),
|
||||
OperationParameterType::RawBody,
|
||||
) => Some(quote! {
|
||||
// Set the content type (this is handled by helper
|
||||
// functions for other MIME types).
|
||||
.header(
|
||||
reqwest::header::CONTENT_TYPE,
|
||||
reqwest::header::HeaderValue::from_static(#mime_type),
|
||||
)
|
||||
.body(body)
|
||||
}),
|
||||
(
|
||||
OperationParameterKind::Body(BodyContentType::Json),
|
||||
OperationParameterType::Type(_),
|
||||
|
@ -1627,21 +1671,42 @@ impl Generator {
|
|||
}
|
||||
}
|
||||
|
||||
OperationParameterType::RawBody => {
|
||||
let err_msg = format!(
|
||||
"conversion to `reqwest::Body` for {} failed",
|
||||
param.name,
|
||||
);
|
||||
OperationParameterType::RawBody => match param.kind {
|
||||
OperationParameterKind::Body(BodyContentType::OctetStream) => {
|
||||
let err_msg = format!(
|
||||
"conversion to `reqwest::Body` for {} failed",
|
||||
param.name,
|
||||
);
|
||||
|
||||
Ok(quote! {
|
||||
pub fn #param_name<B>(mut self, value: B) -> Self
|
||||
where B: std::convert::TryInto<reqwest::Body>
|
||||
{
|
||||
self.#param_name = value.try_into()
|
||||
.map_err(|_| #err_msg.to_string());
|
||||
self
|
||||
}
|
||||
})
|
||||
Ok(quote! {
|
||||
pub fn #param_name<B>(mut self, value: B) -> Self
|
||||
where B: std::convert::TryInto<reqwest::Body>
|
||||
{
|
||||
self.#param_name = value.try_into()
|
||||
.map_err(|_| #err_msg.to_string());
|
||||
self
|
||||
}
|
||||
})
|
||||
},
|
||||
OperationParameterKind::Body(BodyContentType::Text(_)) => {
|
||||
let err_msg = format!(
|
||||
"conversion to `String` for {} failed",
|
||||
param.name,
|
||||
);
|
||||
|
||||
Ok(quote! {
|
||||
pub fn #param_name<V>(mut self, value: V) -> Self
|
||||
where V: std::convert::TryInto<String>
|
||||
{
|
||||
self.#param_name = value
|
||||
.try_into()
|
||||
.map_err(|_| #err_msg.to_string())
|
||||
.map(|v| v.into());
|
||||
self
|
||||
}
|
||||
})
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -2105,6 +2170,41 @@ impl Generator {
|
|||
}?;
|
||||
OperationParameterType::RawBody
|
||||
}
|
||||
BodyContentType::Text(_) => {
|
||||
// For a plain text body, we expect a simple, specific schema:
|
||||
// "schema": {
|
||||
// "type": "string",
|
||||
// }
|
||||
match schema.item(components)? {
|
||||
openapiv3::Schema {
|
||||
schema_data:
|
||||
openapiv3::SchemaData {
|
||||
nullable: false,
|
||||
discriminator: None,
|
||||
default: None,
|
||||
// Other fields that describe or document the
|
||||
// schema are fine.
|
||||
..
|
||||
},
|
||||
schema_kind:
|
||||
openapiv3::SchemaKind::Type(openapiv3::Type::String(
|
||||
openapiv3::StringType {
|
||||
format:
|
||||
openapiv3::VariantOrUnknownOrEmpty::Empty,
|
||||
pattern: None,
|
||||
enumeration,
|
||||
min_length: None,
|
||||
max_length: None,
|
||||
},
|
||||
)),
|
||||
} if enumeration.is_empty() => Ok(()),
|
||||
_ => Err(Error::UnexpectedFormat(format!(
|
||||
"invalid schema for {}: {:?}",
|
||||
content_type, schema
|
||||
))),
|
||||
}?;
|
||||
OperationParameterType::RawBody
|
||||
}
|
||||
BodyContentType::Json | BodyContentType::FormUrlencoded => {
|
||||
// TODO it would be legal to have the encoding field set for
|
||||
// application/x-www-form-urlencoded content, but I'm not sure
|
||||
|
|
|
@ -1754,6 +1754,18 @@ impl Client {
|
|||
builder::Whoami::new(self)
|
||||
}
|
||||
|
||||
///Sends a `PUT` request to `/v1/whoami/name`
|
||||
///
|
||||
///```ignore
|
||||
/// let response = client.whoami_put_name()
|
||||
/// .body(body)
|
||||
/// .send()
|
||||
/// .await;
|
||||
/// ```
|
||||
pub fn whoami_put_name(&self) -> builder::WhoamiPutName {
|
||||
builder::WhoamiPutName::new(self)
|
||||
}
|
||||
|
||||
///Sends a `POST` request to `/v1/worker/bootstrap`
|
||||
///
|
||||
///```ignore
|
||||
|
@ -2355,6 +2367,57 @@ pub mod builder {
|
|||
}
|
||||
}
|
||||
|
||||
///Builder for [`Client::whoami_put_name`]
|
||||
///
|
||||
///[`Client::whoami_put_name`]: super::Client::whoami_put_name
|
||||
#[derive(Debug)]
|
||||
pub struct WhoamiPutName<'a> {
|
||||
client: &'a super::Client,
|
||||
body: Result<reqwest::Body, String>,
|
||||
}
|
||||
|
||||
impl<'a> WhoamiPutName<'a> {
|
||||
pub fn new(client: &'a super::Client) -> Self {
|
||||
Self {
|
||||
client,
|
||||
body: Err("body was not initialized".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn body<V>(mut self, value: V) -> Self
|
||||
where
|
||||
V: std::convert::TryInto<String>,
|
||||
{
|
||||
self.body = value
|
||||
.try_into()
|
||||
.map_err(|_| "conversion to `String` for body failed".to_string())
|
||||
.map(|v| v.into());
|
||||
self
|
||||
}
|
||||
|
||||
///Sends a `PUT` request to `/v1/whoami/name`
|
||||
pub async fn send(self) -> Result<ResponseValue<()>, Error<()>> {
|
||||
let Self { client, body } = self;
|
||||
let body = body.map_err(Error::InvalidRequest)?;
|
||||
let url = format!("{}/v1/whoami/name", client.baseurl,);
|
||||
let request = client
|
||||
.client
|
||||
.put(url)
|
||||
.header(
|
||||
reqwest::header::CONTENT_TYPE,
|
||||
reqwest::header::HeaderValue::from_static("text/plain"),
|
||||
)
|
||||
.body(body)
|
||||
.build()?;
|
||||
let result = client.client.execute(request).await;
|
||||
let response = result?;
|
||||
match response.status().as_u16() {
|
||||
200u16 => Ok(ResponseValue::empty(response)),
|
||||
_ => Err(Error::UnexpectedResponse(response)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///Builder for [`Client::worker_bootstrap`]
|
||||
///
|
||||
///[`Client::worker_bootstrap`]: super::Client::worker_bootstrap
|
||||
|
|
|
@ -1754,6 +1754,18 @@ impl Client {
|
|||
builder::Whoami::new(self)
|
||||
}
|
||||
|
||||
///Sends a `PUT` request to `/v1/whoami/name`
|
||||
///
|
||||
///```ignore
|
||||
/// let response = client.whoami_put_name()
|
||||
/// .body(body)
|
||||
/// .send()
|
||||
/// .await;
|
||||
/// ```
|
||||
pub fn whoami_put_name(&self) -> builder::WhoamiPutName {
|
||||
builder::WhoamiPutName::new(self)
|
||||
}
|
||||
|
||||
///Sends a `POST` request to `/v1/worker/bootstrap`
|
||||
///
|
||||
///```ignore
|
||||
|
@ -2355,6 +2367,57 @@ pub mod builder {
|
|||
}
|
||||
}
|
||||
|
||||
///Builder for [`Client::whoami_put_name`]
|
||||
///
|
||||
///[`Client::whoami_put_name`]: super::Client::whoami_put_name
|
||||
#[derive(Debug)]
|
||||
pub struct WhoamiPutName<'a> {
|
||||
client: &'a super::Client,
|
||||
body: Result<reqwest::Body, String>,
|
||||
}
|
||||
|
||||
impl<'a> WhoamiPutName<'a> {
|
||||
pub fn new(client: &'a super::Client) -> Self {
|
||||
Self {
|
||||
client,
|
||||
body: Err("body was not initialized".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn body<V>(mut self, value: V) -> Self
|
||||
where
|
||||
V: std::convert::TryInto<String>,
|
||||
{
|
||||
self.body = value
|
||||
.try_into()
|
||||
.map_err(|_| "conversion to `String` for body failed".to_string())
|
||||
.map(|v| v.into());
|
||||
self
|
||||
}
|
||||
|
||||
///Sends a `PUT` request to `/v1/whoami/name`
|
||||
pub async fn send(self) -> Result<ResponseValue<()>, Error<()>> {
|
||||
let Self { client, body } = self;
|
||||
let body = body.map_err(Error::InvalidRequest)?;
|
||||
let url = format!("{}/v1/whoami/name", client.baseurl,);
|
||||
let request = client
|
||||
.client
|
||||
.put(url)
|
||||
.header(
|
||||
reqwest::header::CONTENT_TYPE,
|
||||
reqwest::header::HeaderValue::from_static("text/plain"),
|
||||
)
|
||||
.body(body)
|
||||
.build()?;
|
||||
let result = client.client.execute(request).await;
|
||||
let response = result?;
|
||||
match response.status().as_u16() {
|
||||
200u16 => Ok(ResponseValue::empty(response)),
|
||||
_ => Err(Error::UnexpectedResponse(response)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///Builder for [`Client::worker_bootstrap`]
|
||||
///
|
||||
///[`Client::worker_bootstrap`]: super::Client::worker_bootstrap
|
||||
|
|
|
@ -20,6 +20,7 @@ impl Cli {
|
|||
CliCommand::TaskOutputDownload => Self::cli_task_output_download(),
|
||||
CliCommand::UserCreate => Self::cli_user_create(),
|
||||
CliCommand::Whoami => Self::cli_whoami(),
|
||||
CliCommand::WhoamiPutName => Self::cli_whoami_put_name(),
|
||||
CliCommand::WorkerBootstrap => Self::cli_worker_bootstrap(),
|
||||
CliCommand::WorkerPing => Self::cli_worker_ping(),
|
||||
CliCommand::WorkerTaskAppend => Self::cli_worker_task_append(),
|
||||
|
@ -151,6 +152,10 @@ impl Cli {
|
|||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub fn cli_whoami_put_name() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
}
|
||||
|
||||
pub fn cli_worker_bootstrap() -> clap::Command {
|
||||
clap::Command::new("")
|
||||
.arg(
|
||||
|
@ -348,6 +353,9 @@ impl<T: CliOverride> Cli<T> {
|
|||
CliCommand::Whoami => {
|
||||
self.execute_whoami(matches).await;
|
||||
}
|
||||
CliCommand::WhoamiPutName => {
|
||||
self.execute_whoami_put_name(matches).await;
|
||||
}
|
||||
CliCommand::WorkerBootstrap => {
|
||||
self.execute_worker_bootstrap(matches).await;
|
||||
}
|
||||
|
@ -577,6 +585,22 @@ impl<T: CliOverride> Cli<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn execute_whoami_put_name(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.whoami_put_name();
|
||||
self.over
|
||||
.execute_whoami_put_name(matches, &mut request)
|
||||
.unwrap();
|
||||
let result = request.send().await;
|
||||
match result {
|
||||
Ok(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
Err(r) => {
|
||||
println!("success\n{:#?}", r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute_worker_bootstrap(&self, matches: &clap::ArgMatches) {
|
||||
let mut request = self.client.worker_bootstrap();
|
||||
if let Some(value) = matches.get_one::<String>("bootstrap") {
|
||||
|
@ -859,6 +883,14 @@ pub trait CliOverride {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_whoami_put_name(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
request: &mut builder::WhoamiPutName,
|
||||
) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_worker_bootstrap(
|
||||
&self,
|
||||
matches: &clap::ArgMatches,
|
||||
|
@ -938,6 +970,7 @@ pub enum CliCommand {
|
|||
TaskOutputDownload,
|
||||
UserCreate,
|
||||
Whoami,
|
||||
WhoamiPutName,
|
||||
WorkerBootstrap,
|
||||
WorkerPing,
|
||||
WorkerTaskAppend,
|
||||
|
@ -961,6 +994,7 @@ impl CliCommand {
|
|||
CliCommand::TaskOutputDownload,
|
||||
CliCommand::UserCreate,
|
||||
CliCommand::Whoami,
|
||||
CliCommand::WhoamiPutName,
|
||||
CliCommand::WorkerBootstrap,
|
||||
CliCommand::WorkerPing,
|
||||
CliCommand::WorkerTaskAppend,
|
||||
|
|
|
@ -403,6 +403,40 @@ pub mod operations {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct WhoamiPutNameWhen(httpmock::When);
|
||||
impl WhoamiPutNameWhen {
|
||||
pub fn new(inner: httpmock::When) -> Self {
|
||||
Self(
|
||||
inner
|
||||
.method(httpmock::Method::PUT)
|
||||
.path_matches(regex::Regex::new("^/v1/whoami/name$").unwrap()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> httpmock::When {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn body(self, value: String) -> Self {
|
||||
Self(self.0.body(value))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WhoamiPutNameThen(httpmock::Then);
|
||||
impl WhoamiPutNameThen {
|
||||
pub fn new(inner: httpmock::Then) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> httpmock::Then {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn ok(self) -> Self {
|
||||
Self(self.0.status(200u16))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WorkerBootstrapWhen(httpmock::When);
|
||||
impl WorkerBootstrapWhen {
|
||||
pub fn new(inner: httpmock::When) -> Self {
|
||||
|
@ -743,6 +777,9 @@ pub trait MockServerExt {
|
|||
fn whoami<F>(&self, config_fn: F) -> httpmock::Mock
|
||||
where
|
||||
F: FnOnce(operations::WhoamiWhen, operations::WhoamiThen);
|
||||
fn whoami_put_name<F>(&self, config_fn: F) -> httpmock::Mock
|
||||
where
|
||||
F: FnOnce(operations::WhoamiPutNameWhen, operations::WhoamiPutNameThen);
|
||||
fn worker_bootstrap<F>(&self, config_fn: F) -> httpmock::Mock
|
||||
where
|
||||
F: FnOnce(operations::WorkerBootstrapWhen, operations::WorkerBootstrapThen);
|
||||
|
@ -890,6 +927,18 @@ impl MockServerExt for httpmock::MockServer {
|
|||
})
|
||||
}
|
||||
|
||||
fn whoami_put_name<F>(&self, config_fn: F) -> httpmock::Mock
|
||||
where
|
||||
F: FnOnce(operations::WhoamiPutNameWhen, operations::WhoamiPutNameThen),
|
||||
{
|
||||
self.mock(|when, then| {
|
||||
config_fn(
|
||||
operations::WhoamiPutNameWhen::new(when),
|
||||
operations::WhoamiPutNameThen::new(then),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn worker_bootstrap<F>(&self, config_fn: F) -> httpmock::Mock
|
||||
where
|
||||
F: FnOnce(operations::WorkerBootstrapWhen, operations::WorkerBootstrapThen),
|
||||
|
|
|
@ -532,6 +532,29 @@ impl Client {
|
|||
}
|
||||
}
|
||||
|
||||
///Sends a `PUT` request to `/v1/whoami/name`
|
||||
pub async fn whoami_put_name<'a>(
|
||||
&'a self,
|
||||
body: String,
|
||||
) -> Result<ResponseValue<()>, Error<()>> {
|
||||
let url = format!("{}/v1/whoami/name", self.baseurl,);
|
||||
let request = self
|
||||
.client
|
||||
.put(url)
|
||||
.header(
|
||||
reqwest::header::CONTENT_TYPE,
|
||||
reqwest::header::HeaderValue::from_static("text/plain"),
|
||||
)
|
||||
.body(body)
|
||||
.build()?;
|
||||
let result = self.client.execute(request).await;
|
||||
let response = result?;
|
||||
match response.status().as_u16() {
|
||||
200u16 => Ok(ResponseValue::empty(response)),
|
||||
_ => Err(Error::UnexpectedResponse(response)),
|
||||
}
|
||||
}
|
||||
|
||||
///Sends a `POST` request to `/v1/worker/bootstrap`
|
||||
pub async fn worker_bootstrap<'a>(
|
||||
&'a self,
|
||||
|
|
|
@ -252,6 +252,25 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/v1/whoami/name": {
|
||||
"put": {
|
||||
"operationId": "whoami_put_name",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "successful operation"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/v1/worker/bootstrap": {
|
||||
"post": {
|
||||
"operationId": "worker_bootstrap",
|
||||
|
|
Loading…
Reference in New Issue