openapi / actix-swagger Goto Github PK
View Code? Open in Web Editor NEWSwagger code generator for actix-web framework
Home Page: https://crates.io/crates/cargo-swagg
License: MIT License
Swagger code generator for actix-web framework
Home Page: https://crates.io/crates/cargo-swagg
License: MIT License
Hi , i m new in rust so i m trying to learn a bit possibilities.
It is possible to create a macro over a method permitting to generate the swagger code analyzing function input/output and mapping ?
I m searching a simple rest example for a demo in my office. Do you know if i can find it on web?
If/when the actix-web dependency is updated to v3, the dependency in Cargo.toml should ideally be:
- actix-web = "3.0.2"
+ actix-web = { version = "3.0.2", default-features = false }
I'm trying to use it as per docs, but getting error at the stage of parsing arguments:
> cargo swagg ./AlpacaDeviceAPI_v1.yaml --out-file src/api.rs
error: Found argument './AlpacaDeviceAPI_v1.yaml' which wasn't expected, or isn't valid in this context
USAGE:
cargo-swagg.exe [OPTIONS] <source>
For more information try --help
Am I missing something?
pub mod parameters {
use actix_web::{FromRequest, HttpRequest};
use serde::Serialize;
#[derive(Debug, Serialize, Clone)]
struct ParseHeaderError {
error: String,
message: String,
}
fn extract_header(req: &HttpRequest, name: String) -> Result<String, ParseHeaderError> {
let header_error = ParseHeaderError {
error: "header_required".to_string(),
message: format!("header '{}' is required", name),
};
let header = req.headers().get(name).ok_or(header_error.clone())?;
let value = header.to_str().map_err(|_| header_error)?.to_string();
Ok(value)
}
pub struct AccessToken(pub String);
impl FromRequest for AccessToken {
type Config = ();
type Error = actix_web::Error;
type Future = futures::future::Ready<Result<Self, Self::Error>>;
#[inline]
fn from_request(req: &HttpRequest, _: &mut actix_web::dev::Payload) -> Self::Future {
match extract_header(&req, "X-Access-Token".to_string()) {
Ok(value) => futures::future::ok(AccessToken(value)),
Err(reason) => match serde_json::to_string(&reason) {
Ok(json) => futures::future::err(actix_web::error::ErrorBadRequest(json)),
Err(error) => {
futures::future::err(actix_web::error::ErrorInternalServerError(error))
}
},
}
}
}
}
error[E0432]: unresolved imports `actix_web::http::header::IntoHeaderValue`, `actix_web::http::HeaderName`, `actix_web::http::HeaderValue`
--> /home/rafal/.cargo/registry/src/github.com-1ecc6299db9ec823/actix-swagger-0.3.0/src/lib.rs:10:26
|
10 | http::header::{self, IntoHeaderValue},
| ^^^^^^^^^^^^^^^
| |
| no `IntoHeaderValue` in `http::header`
| help: a similar name exists in the module: `TryIntoHeaderValue`
11 | http::{HeaderName, HeaderValue},
| ^^^^^^^^^^ ^^^^^^^^^^^ no `HeaderValue` in `http`
| |
| no `HeaderName` in `http`
Unless I'm missing something, the spec supports array
type but the parse_schema
function just fails with unimplemented
:
I didn't see any other bug tracking this, so thought I'd start one.
Thanks!
pub mod session_get {
use super::responses;
use actix_swagger::ContentType;
use actix_web::http::StatusCode;
use serde::Serialize;
pub type Answer = actix_swagger::Answer<'static, Response>;
#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum Response {
Ok(responses::SessionGetSuccess),
Unauthorized,
Unexpected,
}
impl Into<Answer> for Response {
#[inline]
fn into(self) -> Answer {
let status = match self {
Self::Ok(_) => StatusCode::OK,
Self::Unauthorized => StatusCode::UNAUTHORIZED,
Self::Unexpected => StatusCode::INTERNAL_SERVER_ERROR,
};
let content_type = match self {
Self::Ok(_) => Some(ContentType::Json),
Self::Unauthorized => None,
Self::Unexpected => None,
};
Answer::new(self).status(status).content_type(content_type)
}
}
rel #17
pub mod session_get {
use super::responses;
use actix_swagger::ContentType;
use actix_web::http::StatusCode;
use serde::Serialize;
pub type Answer = actix_swagger::Answer<'static, Response>;
#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum Response {
Ok(responses::SessionGetSuccess),
Unauthorized,
Unexpected,
}
impl Response {
#[inline]
pub fn into_answer<'a>(self) -> Answer {
let status = match self {
Self::Ok(_) => StatusCode::OK,
Self::Unauthorized => StatusCode::UNAUTHORIZED,
Self::Unexpected => StatusCode::INTERNAL_SERVER_ERROR,
};
let content_type = match self {
Self::Ok(_) => Some(ContentType::Json),
Self::Unauthorized => None,
Self::Unexpected => None,
};
Answer::new(self).status(status).content_type(content_type)
}
}
}
Hi,
I have issue to integrate the library. My code is the following:
Main:
fn main() {
HttpServer::new(move || {
App::new()
.data(AppState {
pool: pool.clone(),
log: log.clone(),
})
.wrap(middleware::Logger::default())
.route("/", web::get().to(status))
.route("/test{_:/?}", web::get().to(test))
})
.bind(format!("{}:{}", config.server.host, config.server.port))?
.run()
.await
}
Handler:
pub async fn status() -> Result<impl Responder, AppError> {
Ok(web::HttpResponse::Ok().json(Status {
status: "Up".to_string(),
}))
}
pub async fn test(state: web::Data<AppState>) -> Result<impl Responder, AppError> {
let sublog = state.log.new(o!("handler" => "test"));
let client: Client = get_client(state.pool.clone(), sublog.clone()).await?;
let result = db::get_test(&client).await;
result
.map(|test| HttpResponse::Ok().json(test))
.map_err(log_error(sublog))
}
https://t.me/rustlang_ru/285259 https://teleg.one/rustlang_ru/285259 (Russian)
Translated:
made a sufficient version of the code generator for actix_swagger.
Generates this: https://github.com/sergeysova/actix-swagger/blob/master/cargo-swagg/out.rs.
In principle, it's enough to write applications, I have about this kind of communication. But there is no execution of the contract of request handlers here. There is no obligation to correctly process the request body in the handler. I could not find a way to do it through actix_web::dev::Factory
.
Here is a discussion issue: actix/actix-web#1339.
By actix-swagger:
I will now write a conversion of yaml structures to the code generator format. Most likely there will be a HashMap/BTreeMap bundle to correctly resolve links. The first implementation will be very stupid and will be able to work only with links in requestBody, params and so on. There will also be no support for importing from files.
What do you need help with?
Project goals:
To input the openapi3 specification, to get the generated code at the output that does not need to be corrected by hand. At the same time, you may commit it into the repo and watch changes in the guitar after manual re-generation. You can take it to a separate crate, re-generate it into ci and deploy the crate of the new version. Update it in the project when it is convenient.
How to use:
actix_swagger::Response
is used as the actix_web::Response
analogue with explicit response types to verify that the request handler implements the contract.We have to figure this out:
how to get the request handler to execute the contract on the request body, query, and headers.
I was thinking of making my analogue actix_web::dev::Factory, the so-called BoundFactory. Which would prescribe the implementation for Fn. BoundFactory is a trait that requires a function to have several mandatory parameters for specific types.
In this way, handlers could have the first arguments of clear types, such as request_body: request_bodies::Example, query: session_create::Query, and the rest of the arguments would be derived types, so you can pull out the necessary things, such as actix_web::web::Data.
But I'm relying on the privacy of Handler - actix/actix-web#1339.
I did not understand how to implement BoundFactory or even achieve the goal through Handle trait (actix/actix-web#1275).
I'd be so grateful for any help.
We can generate type
alias. Instead of this:
pub async fn route(
app: web::Data<crate::App>,
) -> Answer<'static, register_request::Response> {
Generate result type:
pub async fn route(
app: web::Data<crate::App>,
) -> register_request::Answer {
Implemented in the printer:
schema:
properties:
demo:
type: string
x-rust-type: crate::app::MyType
Output:
struct Example {
demo: Option<crate::app::MyType>,
}
Required:
schema:
required:
- demo
properties:
demo:
type: string
x-rust-type: crate::app::MyType
Output:
struct Example {
demo: crate::app::MyType,
}
Instead of passing body, header, path extractors to a route:
pub async fn route(
body: web::Json<request_bodies::Register>,
app: web::Data<crate::App>,
) -> Answer<'static, register_request::Response> {
Generate a single extractor for all data. Router implementation doesn't know about API details:
pub async fn route(
params: paths::register_request::Params,
app: web::Data<crate::App>,
) -> Answer<'static, register_request::Response> {
Single attribute:
schema:
properties:
demo:
type: string
x-rust-attribute: serde(skip_serializing_if = "Option::is_none")
Should generate something like:
struct Example {
#[serde(skip_serializing_if = "Option::is_none")]
demo: String,
}
Multiple attributes:
schema:
properties:
demo:
type: string
x-rust-attribute:
- serde(skip_serializing_if = “Option::is_none”)
- validate(url)
Output:
struct Example {
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(url)]
demo: String,
}
Openapi.yaml
x-generator: NSwag v13.3.0.0 (NJsonSchema v10.1.11.0 (Newtonsoft.Json v12.0.0.0))
openapi: 3.0.0
info:
title: Settings API
description: Settings and presets service
version: v1
servers:
- url: 'https://myUrl'
paths:
/:
get:
tags:
- About
operationId: About_Get
responses:
'200':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/MessageModel'
/settings/api:
get:
tags:
- About
operationId: About_Get2
responses:
'200':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/MessageModel'
/Healthcheck:
get:
tags:
- Healthcheck
operationId: Healthcheck_Get
responses:
'200':
description: ''
/settings/api/Healthcheck:
get:
tags:
- Healthcheck
operationId: Healthcheck_Get2
responses:
'200':
description: ''
components:
schemas:
MessageModel:
type: object
additionalProperties: false
properties:
message:
type: string
nullable: true
Integration:
type: object
additionalProperties: false
properties:
id:
type: string
format: guid
createdBy:
type: string
format: guid
createdOn:
type: string
format: date-time
modifiedBy:
type: string
format: guid
nullable: true
modifiedOn:
type: string
format: date-time
nullable: true
deletedReason:
type: string
nullable: true
deletedBy:
type: string
format: guid
nullable: true
deletedOn:
type: string
format: date-time
nullable: true
companyId:
type: string
format: guid
title:
type: string
nullable: true
securitySchemes:
Bearer:
type: apiKey
description: Please insert Auth token into field
name: Authorization
in: header
security:
- Bearer: []
Command:
cargo-swagg ./openapi.yaml --out-file ./src/api.rs
Expected result:
Got API
Actual result:
#![allow(dead_code, unused_imports)]
pub mod api {
#[doc = "Settings and presets service"]
pub struct SettingsApi {
api: actix_swagger::Api,
}
impl SettingsApi {
pub fn new() -> Self {
Self {
api: actix_swagger::Api::new(),
}
}
}
impl Default for SettingsApi {
fn default() -> Self {
let api = Self::new();
api
}
}
impl actix_web::dev::HttpServiceFactory for SettingsApi {
fn register(self, config: &mut actix_web::dev::AppService) {
self.api.register(config);
}
}
use super::paths;
use actix_swagger::{Answer, Method};
use actix_web::{dev::Factory, FromRequest};
use std::future::Future;
impl SettingsApi {}
}
pub mod components {
pub mod parameters {
use serde::{Deserialize, Serialize};
}
pub mod request_bodies {
use serde::{Deserialize, Serialize};
}
pub mod responses {
use serde::{Deserialize, Serialize};
}
}
pub mod paths {
use super::components::{parameters, responses};
}
I think it worth adding
#![allow(clippy::all, clippy::restriction, clippy::pedantic, clippy::nursery, clippy::cargo)]
to the existing #![allow(dead_code, unused_imports)]
disabled warnings.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.