Skip to content

Commit

Permalink
feat: add api example
Browse files Browse the repository at this point in the history
  • Loading branch information
slavik-pastushenko committed Nov 15, 2023
1 parent 37d9358 commit e17b29f
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 6 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,10 @@ fn main() {

## Examples

[Provides a command-line interface (CLI) for interacting with blockchain](https://github.com/slavik-pastushenko/blockchain-rust/tree/main/examples/cli)
Explore the capabilities of this blockchain implementation through a set of examples:

- [CLI for interacting with the blockchain](https://github.com/slavik-pastushenko/blockchain-rust/tree/main/examples/cli)
- [API for interacting with the blockchain using axum](https://github.com/slavik-pastushenko/blockchain-rust/tree/main/examples/api-axum)

## Contributing

Expand Down
16 changes: 16 additions & 0 deletions examples/api-axum/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "api-axum"
version = "0.0.0"
edition = "2021"
publish = false

[[bin]]
name = "api_axum"
path = "src/main.rs"

[dependencies]
axum = "0.6.20"
blockchain-cli = { path = "../.." }
serde = { version = "1.0.192", features = ["derive"] }
serde_json = "1.0.108"
tokio = { version = "1.34.0", features = ["full"] }
145 changes: 145 additions & 0 deletions examples/api-axum/postman-collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
{
"info": {
"_postman_id": "7ae8d19b-3ca0-4f23-a7cf-222933b9ecdf",
"name": "API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "9242892"
},
"item": [
{
"name": "Wallet",
"item": [
{
"name": "Get balance",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{api_url}}/wallet/balance?address={{wallet_address}}",
"host": [
"{{api_url}}"
],
"path": [
"wallet",
"balance"
],
"query": [
{
"key": "address",
"value": "{{wallet_address}}"
}
]
}
},
"response": []
},
{
"name": "Create wallet",
"event": [
{
"listen": "test",
"script": {
"exec": [
"const response = pm.response.json()",
"",
"pm.environment.set(\"wallet_address\", response.data);"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"email\": \"my@email.com\"\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{api_url}}/wallet/create",
"host": [
"{{api_url}}"
],
"path": [
"wallet",
"create"
]
}
},
"response": []
}
]
},
{
"name": "Transaction",
"item": [
{
"name": "Get transactions",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{api_url}}/transactions",
"host": [
"{{api_url}}"
],
"path": [
"transactions"
]
}
},
"response": []
},
{
"name": "Get transaction",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{api_url}}/transactions/hash",
"host": [
"{{api_url}}"
],
"path": [
"transactions",
"hash"
]
}
},
"response": []
},
{
"name": "Add transaction",
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\n \"from\": \"{{wallet_address}}\",\n \"to\": \"hwU2XS03Y5VEnqpDkkIaL4rlMLG0mbZ8UZ66P4X6Uh\",\n \"amount\": 1.25\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{api_url}}/transactions",
"host": [
"{{api_url}}"
],
"path": [
"transactions"
]
}
},
"response": []
}
]
}
]
}
94 changes: 94 additions & 0 deletions examples/api-axum/src/handlers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use axum::{
extract::{Path, Query, State},
http::StatusCode,
response::IntoResponse,
Json,
};
use blockchain::Chain;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::sync::{Arc, Mutex};

#[derive(Clone)]
pub struct AppState {
pub chain: Arc<Mutex<Chain>>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CreateWallet {
pub email: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct AddTransaction {
pub from: String,
pub to: String,
pub amount: f64,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct GetWalletBalance {
pub address: String,
}

pub async fn create_wallet(
State(state): State<AppState>,
Json(body): Json<CreateWallet>,
) -> impl IntoResponse {
let mut chain = state.chain.lock().unwrap();
let address = chain.create_wallet(body.email);

(StatusCode::OK, Json(json!({ "data": address })))
}

pub async fn get_wallet_balance(
State(state): State<AppState>,
Query(params): Query<GetWalletBalance>,
) -> impl IntoResponse {
let chain = state.chain.lock().unwrap();
let balance = chain.get_wallet_balance(params.address);

if balance.is_none() {
return (
StatusCode::NOT_FOUND,
Json(json!({ "message": "Wallet is not found" })),
);
}

(StatusCode::OK, Json(json!({ "data": balance })))
}

pub async fn get_transactions(State(state): State<AppState>) -> impl IntoResponse {
let mut chain = state.chain.lock().unwrap();
let transactions = chain.get_transactions();

(StatusCode::OK, Json(json!({ "data": transactions })))
}

pub async fn get_transaction(
State(state): State<AppState>,
Path(hash): Path<String>,
) -> impl IntoResponse {
let chain = state.chain.lock().unwrap();
let transaction = chain.get_transaction(hash);

if transaction.is_none() {
return (
StatusCode::NOT_FOUND,
Json(json!({ "message": "Transaction is not found" })),
);
}

(StatusCode::OK, Json(json!({ "data": transaction })))
}

pub async fn add_transaction(
State(state): State<AppState>,
Json(body): Json<AddTransaction>,
) -> impl IntoResponse {
let mut chain = state.chain.lock().unwrap();

let result = chain.add_transaction(body.from, body.to, body.amount);

(StatusCode::OK, Json(json!({ "data": result })))
}
38 changes: 38 additions & 0 deletions examples/api-axum/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use crate::handlers::AppState;
use axum::{
routing::{get, post},
Router,
};
use blockchain::Chain;
use std::{
net::SocketAddr,
sync::{Arc, Mutex},
};

mod handlers;

#[tokio::main]
async fn main() {
let chain = Chain::new(2.0, 100.0, 0.01);

let state = AppState {
chain: Arc::new(Mutex::new(chain)),
};

let app = Router::new()
.route("/transactions/:hash", get(handlers::get_transaction))
.route("/transactions", get(handlers::get_transactions))
.route("/transactions", post(handlers::add_transaction))
.route("/wallet/balance", get(handlers::get_wallet_balance))
.route("/wallet/create", post(handlers::create_wallet))
.with_state(state);

let address = SocketAddr::from(([0, 0, 0, 0], 7878));

println!("Server is running on {}", address);

axum::Server::bind(&address)
.serve(app.into_make_service())
.await
.unwrap();
}
10 changes: 5 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::fmt::Write;
use std::iter;

/// Exchange of assets between two parties
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Transaction {
/// Transaction hash
pub hash: String,
Expand All @@ -31,7 +31,7 @@ pub struct Transaction {
}

/// Identifier of a particular block on an entire blockchain
#[derive(Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BlockHeader {
/// Timestamp at which a block was mined
pub timestamp: i64,
Expand All @@ -50,7 +50,7 @@ pub struct BlockHeader {
}

/// Data storage in a blockchain
#[derive(Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Block {
/// Information about the block and the miner
pub header: BlockHeader,
Expand All @@ -63,7 +63,7 @@ pub struct Block {
}

/// Wallet
#[derive(Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Wallet {
/// Unique email address associated with the wallet.
pub email: String,
Expand All @@ -76,7 +76,7 @@ pub struct Wallet {
}

/// Blockchain
#[derive(Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Chain {
/// Chain of blocks
pub chain: Vec<Block>,
Expand Down

0 comments on commit e17b29f

Please sign in to comment.