* Re: [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api
@ 2021-09-29 5:19 Dietmar Maurer
0 siblings, 0 replies; 3+ messages in thread
From: Dietmar Maurer @ 2021-09-29 5:19 UTC (permalink / raw)
To: Proxmox Backup Server development discussion, Dominik Csapak
Is it possible to avoid the dev-dependency to pbs-runtime?
(We want to split out proxmox-rest-server, so we cannot use 'pbs-runtime')
> diff --git a/proxmox-rest-server/Cargo.toml b/proxmox-rest-server/Cargo.toml
> index b0e53d19..a6d25b8b 100644
> --- a/proxmox-rest-server/Cargo.toml
> +++ b/proxmox-rest-server/Cargo.toml
> @@ -5,6 +5,11 @@ authors = ["Proxmox Support Team <support@proxmox.com>"]
> edition = "2018"
> description = "REST server implementation"
>
> +# for example
> +[dev-dependencies]
> +proxmox = { version = "0.13.4", features = ["router","api-macro"] }
> +pbs-runtime = { path = "../pbs-runtime" }
^ permalink raw reply [flat|nested] 3+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v2 0/3] add rest_server example @ 2021-09-28 9:11 Dominik Csapak 2021-09-28 9:11 ` [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api Dominik Csapak 0 siblings, 1 reply; 3+ messages in thread From: Dominik Csapak @ 2021-09-28 9:11 UTC (permalink / raw) To: pbs-devel adds a rest_server example to the proxmox-rest-server crate. changes from v1: * added patches so we can remove the (now) unused srt/tools/async_io.rs. this enables us to don't have any dependencies on the top-level crate for the example, and we can move it into proxmox-rest-server * use hypers AddrStream for the example Dominik Csapak (3): rest-server: use hypers AddrStream for proxmox-backup-api remove tools/async_io.rs examples: add example for a simple rest server with a small api proxmox-rest-server/Cargo.toml | 5 + proxmox-rest-server/examples/rest_server.rs | 219 ++++++++++++++++++++ proxmox-rest-server/src/rest.rs | 18 +- src/bin/proxmox-backup-api.rs | 3 +- src/tools/async_io.rs | 83 -------- src/tools/mod.rs | 1 - 6 files changed, 234 insertions(+), 95 deletions(-) create mode 100644 proxmox-rest-server/examples/rest_server.rs delete mode 100644 src/tools/async_io.rs -- 2.30.2 ^ permalink raw reply [flat|nested] 3+ messages in thread
* [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api 2021-09-28 9:11 [pbs-devel] [PATCH proxmox-backup v2 0/3] add rest_server example Dominik Csapak @ 2021-09-28 9:11 ` Dominik Csapak 2021-09-29 6:41 ` Thomas Lamprecht 0 siblings, 1 reply; 3+ messages in thread From: Dominik Csapak @ 2021-09-28 9:11 UTC (permalink / raw) To: pbs-devel show how to generally start a daemon that serves a rest api + index page api calls are: / GET listing /ping GET returns "pong" /items GET lists existing items POST lets user create new items /items/{id} GET returns the content of a single item PUT updates an item DELETE deletes an item Contains a small dummy user/authinfo Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> --- changes from v1: * use hyper::server::conn::AddrIncoming::from_listener proxmox-rest-server/Cargo.toml | 5 + proxmox-rest-server/examples/rest_server.rs | 219 ++++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 proxmox-rest-server/examples/rest_server.rs diff --git a/proxmox-rest-server/Cargo.toml b/proxmox-rest-server/Cargo.toml index b0e53d19..a6d25b8b 100644 --- a/proxmox-rest-server/Cargo.toml +++ b/proxmox-rest-server/Cargo.toml @@ -5,6 +5,11 @@ authors = ["Proxmox Support Team <support@proxmox.com>"] edition = "2018" description = "REST server implementation" +# for example +[dev-dependencies] +proxmox = { version = "0.13.4", features = ["router","api-macro"] } +pbs-runtime = { path = "../pbs-runtime" } + [dependencies] anyhow = "1.0" futures = "0.3" diff --git a/proxmox-rest-server/examples/rest_server.rs b/proxmox-rest-server/examples/rest_server.rs new file mode 100644 index 00000000..89efbcb2 --- /dev/null +++ b/proxmox-rest-server/examples/rest_server.rs @@ -0,0 +1,219 @@ +use std::sync::{Arc, Mutex}; +use std::collections::HashMap; + +use anyhow::{bail, format_err, Error}; +use futures::{FutureExt, TryFutureExt}; +use lazy_static::lazy_static; + +use proxmox::api::{api, router::SubdirMap, Router, RpcEnvironmentType, UserInformation}; +use proxmox::list_subdirs_api_method; +use proxmox_rest_server::{ApiAuth, ApiConfig, AuthError, RestServer}; + +// Create a Dummy User info and auth system +// Normally this would check and authenticate the user +struct DummyUserInfo; + +impl UserInformation for DummyUserInfo { + fn is_superuser(&self, _userid: &str) -> bool { + true + } + fn is_group_member(&self, _userid: &str, group: &str) -> bool { + group == "Group" + } + fn lookup_privs(&self, _userid: &str, _path: &[&str]) -> u64 { + u64::MAX + } +} + +struct DummyAuth; + +impl ApiAuth for DummyAuth { + fn check_auth( + &self, + _headers: &http::HeaderMap, + _method: &hyper::Method, + ) -> Result<(String, Box<dyn UserInformation + Sync + Send>), AuthError> { + // get some global/cached userinfo + let userinfo = DummyUserInfo; + // Do some user checks, e.g. cookie/csrf + Ok(("User".to_string(), Box::new(userinfo))) + } +} + +// this should return the index page of the webserver +// iow. what the user browses to + +fn get_index( + _auth_id: Option<String>, + _language: Option<String>, + _api: &ApiConfig, + _parts: http::request::Parts, +) -> http::Response<hyper::Body> { + // build an index page + http::Response::builder() + .body("hello world".into()) + .unwrap() +} + +// a few examples on how to do api calls with the Router + +#[api] +/// A simple ping method. returns "pong" +fn ping() -> Result<String, Error> { + Ok("pong".to_string()) +} + +lazy_static! { + static ref ITEM_MAP: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new()); +} + +#[api] +/// Lists all current items +fn list_items() -> Result<Vec<String>, Error> { + Ok(ITEM_MAP.lock().unwrap().keys().map(|k| k.clone()).collect()) +} + +#[api( + input: { + properties: { + name: { + type: String, + description: "The name", + }, + value: { + type: String, + description: "The value", + }, + }, + }, +)] +/// creates a new item +fn create_item(name: String, value: String) -> Result<(), Error> { + let mut map = ITEM_MAP.lock().unwrap(); + if map.contains_key(&name) { + bail!("{} already exists", name); + } + + map.insert(name, value); + + Ok(()) +} + +#[api( + input: { + properties: { + name: { + type: String, + description: "The name", + }, + }, + }, +)] +/// returns the value of an item +fn get_item(name: String) -> Result<String, Error> { + ITEM_MAP.lock().unwrap().get(&name).map(|s| s.to_string()).ok_or_else(|| format_err!("no such item '{}'", name)) +} + +#[api( + input: { + properties: { + name: { + type: String, + description: "The name", + }, + value: { + type: String, + description: "The value", + }, + }, + }, +)] +/// updates an item +fn update_item(name: String, value: String) -> Result<(), Error> { + if let Some(val) = ITEM_MAP.lock().unwrap().get_mut(&name) { + *val = value; + } else { + bail!("no such item '{}'", name); + } + Ok(()) +} + +#[api( + input: { + properties: { + name: { + type: String, + description: "The name", + }, + }, + }, +)] +/// deletes an item +fn delete_item(name: String) -> Result<(), Error> { + if ITEM_MAP.lock().unwrap().remove(&name).is_none() { + bail!("no such item '{}'", name); + } + Ok(()) +} + +const ITEM_ROUTER: Router = Router::new() + .get(&API_METHOD_GET_ITEM) + .put(&API_METHOD_UPDATE_ITEM) + .delete(&API_METHOD_DELETE_ITEM); + +const SUBDIRS: SubdirMap = &[ + ( + "items", + &Router::new() + .get(&API_METHOD_LIST_ITEMS) + .post(&API_METHOD_CREATE_ITEM) + .match_all("name", &ITEM_ROUTER) + ), + ( + "ping", + &Router::new() + .get(&API_METHOD_PING) + ), +]; + +const ROUTER: Router = Router::new() + .get(&list_subdirs_api_method!(SUBDIRS)) + .subdirs(SUBDIRS); + +async fn run() -> Result<(), Error> { + + // we first have to configure the api environment (basedir etc.) + + let config = ApiConfig::new( + "/var/tmp/", + &ROUTER, + RpcEnvironmentType::PUBLIC, + Arc::new(DummyAuth {}), + get_index, + )?; + let rest_server = RestServer::new(config); + + // then we have to create a daemon that listens, accepts and serves + // the api to clients + proxmox_rest_server::daemon::create_daemon( + ([127, 0, 0, 1], 65000).into(), + move |listener, ready| { + let incoming = hyper::server::conn::AddrIncoming::from_listener(listener)?; + + Ok(ready + .and_then(|_| hyper::Server::builder(incoming) + .serve(rest_server) + .map_err(Error::from) + ) + .map_err(|err| eprintln!("ERR: {}", err)) + .map(|test| println!("OK: {}", test.is_ok()))) + }, + "example_server", + ).await?; + + Ok(()) +} + +fn main() -> Result<(), Error> { + pbs_runtime::main(run()) +} -- 2.30.2 ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api 2021-09-28 9:11 ` [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api Dominik Csapak @ 2021-09-29 6:41 ` Thomas Lamprecht 0 siblings, 0 replies; 3+ messages in thread From: Thomas Lamprecht @ 2021-09-29 6:41 UTC (permalink / raw) To: Proxmox Backup Server development discussion, Dominik Csapak On 28.09.21 11:11, Dominik Csapak wrote: > show how to generally start a daemon that serves a rest api + index page > > api calls are: well they are prefixed with `/api2` that's really something that we need to make configurable, for any future project I definitively do not want to use the rather confusing /api2 from the start, that can be rather easily adapted in the client's central HTTP request handling code. > / GET listing > /ping GET returns "pong" > /items GET lists existing items > POST lets user create new items > /items/{id} GET returns the content of a single item > PUT updates an item > DELETE deletes an item > > Contains a small dummy user/authinfo > > Signed-off-by: Dominik Csapak <d.csapak@proxmox.com> > --- > changes from v1: > * use hyper::server::conn::AddrIncoming::from_listener > > proxmox-rest-server/Cargo.toml | 5 + > proxmox-rest-server/examples/rest_server.rs | 219 ++++++++++++++++++++ > 2 files changed, 224 insertions(+) > create mode 100644 proxmox-rest-server/examples/rest_server.rs > > diff --git a/proxmox-rest-server/Cargo.toml b/proxmox-rest-server/Cargo.toml > index b0e53d19..a6d25b8b 100644 > --- a/proxmox-rest-server/Cargo.toml > +++ b/proxmox-rest-server/Cargo.toml > @@ -5,6 +5,11 @@ authors = ["Proxmox Support Team <support@proxmox.com>"] > edition = "2018" > description = "REST server implementation" > > +# for example > +[dev-dependencies] > +proxmox = { version = "0.13.4", features = ["router","api-macro"] } 0.13.4 is already outdated, do we even need tho have that version target so strict as else I'd go with "0.13" for now.. > +pbs-runtime = { path = "../pbs-runtime" } > + > [dependencies] > anyhow = "1.0" > futures = "0.3" > diff --git a/proxmox-rest-server/examples/rest_server.rs b/proxmox-rest-server/examples/rest_server.rs > new file mode 100644 > index 00000000..89efbcb2 > --- /dev/null > +++ b/proxmox-rest-server/examples/rest_server.rs > @@ -0,0 +1,219 @@ > +use std::sync::{Arc, Mutex}; > +use std::collections::HashMap; > + > +use anyhow::{bail, format_err, Error}; > +use futures::{FutureExt, TryFutureExt}; > +use lazy_static::lazy_static; > + > +use proxmox::api::{api, router::SubdirMap, Router, RpcEnvironmentType, UserInformation}; > +use proxmox::list_subdirs_api_method; > +use proxmox_rest_server::{ApiAuth, ApiConfig, AuthError, RestServer}; > + > +// Create a Dummy User info and auth system > +// Normally this would check and authenticate the user > +struct DummyUserInfo; > + > +impl UserInformation for DummyUserInfo { > + fn is_superuser(&self, _userid: &str) -> bool { > + true > + } > + fn is_group_member(&self, _userid: &str, group: &str) -> bool { > + group == "Group" > + } > + fn lookup_privs(&self, _userid: &str, _path: &[&str]) -> u64 { > + u64::MAX > + } > +} > + > +struct DummyAuth; > + > +impl ApiAuth for DummyAuth { > + fn check_auth( > + &self, > + _headers: &http::HeaderMap, > + _method: &hyper::Method, > + ) -> Result<(String, Box<dyn UserInformation + Sync + Send>), AuthError> { > + // get some global/cached userinfo > + let userinfo = DummyUserInfo; > + // Do some user checks, e.g. cookie/csrf > + Ok(("User".to_string(), Box::new(userinfo))) > + } > +} > + > +// this should return the index page of the webserver > +// iow. what the user browses to above fits in one line even with 80 cc, and in rust we normally go always for 100cc. > +fn get_index( > + _auth_id: Option<String>, > + _language: Option<String>, > + _api: &ApiConfig, > + _parts: http::request::Parts, > +) -> http::Response<hyper::Body> { > + // build an index page > + http::Response::builder() > + .body("hello world".into()) maybe a mini html thingy with the API description from above + links to the GET calls would be nice to have? > +async fn run() -> Result<(), Error> { > + > + // we first have to configure the api environment (basedir etc.) > + > + let config = ApiConfig::new( > + "/var/tmp/", > + &ROUTER, > + RpcEnvironmentType::PUBLIC, > + Arc::new(DummyAuth {}), > + get_index, > + )?; > + let rest_server = RestServer::new(config); > + > + // then we have to create a daemon that listens, accepts and serves > + // the api to clients > + proxmox_rest_server::daemon::create_daemon( > + ([127, 0, 0, 1], 65000).into(), > + move |listener, ready| { > + let incoming = hyper::server::conn::AddrIncoming::from_listener(listener)?; > + > + Ok(ready > + .and_then(|_| hyper::Server::builder(incoming) > + .serve(rest_server) > + .map_err(Error::from) > + ) > + .map_err(|err| eprintln!("ERR: {}", err)) > + .map(|test| println!("OK: {}", test.is_ok()))) closing parenthesis jungle ^^ Can you at least place the closing one from Ok() one a new line or does rustfmt does not like that? > + }, > + "example_server", > + ).await?; > + > + Ok(()) > +} > + > +fn main() -> Result<(), Error> { > + pbs_runtime::main(run()) here I agree with Dietmar, avoiding that dependency would be nice for the example. > +} > ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2021-09-29 6:42 UTC | newest] Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2021-09-29 5:19 [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api Dietmar Maurer -- strict thread matches above, loose matches on Subject: below -- 2021-09-28 9:11 [pbs-devel] [PATCH proxmox-backup v2 0/3] add rest_server example Dominik Csapak 2021-09-28 9:11 ` [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api Dominik Csapak 2021-09-29 6:41 ` Thomas Lamprecht
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox