From: Dominik Csapak <d.csapak@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api
Date: Tue, 28 Sep 2021 11:11:52 +0200 [thread overview]
Message-ID: <20210928091152.2151682-4-d.csapak@proxmox.com> (raw)
In-Reply-To: <20210928091152.2151682-1-d.csapak@proxmox.com>
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
next prev parent reply other threads:[~2021-09-28 9:11 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
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 1/3] rest-server: use hypers AddrStream for proxmox-backup-api Dominik Csapak
2021-09-29 6:27 ` Thomas Lamprecht
2021-09-28 9:11 ` [pbs-devel] [PATCH proxmox-backup v2 2/3] remove tools/async_io.rs Dominik Csapak
2021-09-28 9:11 ` Dominik Csapak [this message]
2021-09-29 6:41 ` [pbs-devel] [PATCH proxmox-backup v2 3/3] examples: add example for a simple rest server with a small api Thomas Lamprecht
2021-09-29 5:19 Dietmar Maurer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210928091152.2151682-4-d.csapak@proxmox.com \
--to=d.csapak@proxmox.com \
--cc=pbs-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal