feat: Add basic key-value store example

- Added a client and server for a simple key-value store.
- Improved documentation with client and server usage examples.
- Created client and server implementations using the V language.
This commit is contained in:
Mahmoud Emad
2025-03-06 13:32:57 +02:00
parent ae7e7ecb84
commit d2c1be5396
5 changed files with 153 additions and 5 deletions

23
examples/data/ourdb_client.vsh Executable file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
// Please note that before running this script you need to run the server first
// See examples/data/ourdb_server.vsh
import freeflowuniverse.herolib.data.ourdb
import os
mut client := ourdb.new_client(
port: 3000
host: 'localhost'
)!
set := client.set('hello')!
get := client.get(set.id)!
assert set.id == get.id
println('Set result: ${set}')
println('Get result: ${get}')
// test delete functionality
client.delete(set.id)!

View File

@@ -4,7 +4,7 @@ import freeflowuniverse.herolib.data.ourdb
import os import os
mut server := ourdb.new_server( mut server := ourdb.new_server(
port: 9000 port: 3000
allowed_hosts: ['localhost'] allowed_hosts: ['localhost']
allowed_operations: ['set', 'get', 'delete'] allowed_operations: ['set', 'get', 'delete']
secret_key: 'secret' secret_key: 'secret'

35
lib/data/ourdb/CLIENT.md Normal file
View File

@@ -0,0 +1,35 @@
# OurDB Client
## Overview
This client is created to interact with an OurDB server.
## Prerequisites
Before running the client script, ensure that the OurDB server is up and running. You can start the server by following the instructions in the [OurDB Server README](./SERVER.md).
## Installation
Ensure you have the V programming language installed. You can download it from [vlang.io](https://vlang.io/).
## Running the Client
Once the OurDB server is running, execute the client script:
```sh
examples/data/ourdb_client.vsh
```
Alternatively, you can run it using V:
```sh
v -enable-globals run ourdb_client.vsh
```
## How It Works
1. Connects to the OurDB server on `localhost:3000`.
2. Sets a record with the value `hello`.
3. Retrieves the record by ID and verifies the stored value.
4. Deletes the record.
## Example Output
```
Set result: { id: 1, value: 'hello' }
Get result: { id: 1, value: 'hello' }
```

90
lib/data/ourdb/client.v Normal file
View File

@@ -0,0 +1,90 @@
module ourdb
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.httpconnection
import json
pub struct OurDBClient {
pub mut:
conn httpconnection.HTTPConnection
port int // Server port, default is 3000
host string
}
@[params]
pub struct OurDBClientArgs {
pub mut:
port int = 3000 // Server port, default is 3000
host string = 'localhost' // Server host
}
struct Response[T] {
message string // Success message
data T // Response data
}
pub fn new_client(args OurDBClientArgs) !OurDBClient {
mut client := OurDBClient{
port: args.port
host: args.host
}
client.conn = client.connection()!
console.print_green('Client started')
return client
}
fn (mut client OurDBClient) connection() !&httpconnection.HTTPConnection {
mut http := httpconnection.new(
name: 'ourdb_client'
url: 'http://${client.host}:${client.port}'
cache: true
retry: 3
)!
client.conn = http
return http
}
// Sets a value in the database
pub fn (mut client OurDBClient) set(data string) !KeyValueData {
mut request_body := json.encode({
'value': data
})
req := httpconnection.Request{
prefix: 'set'
method: .post
data: request_body
}
mut http := client.connection()!
response := http.post_json_str(req)!
mut decoded_response := json.decode(Response[KeyValueData], response)!
return decoded_response.data
}
// Gets a value in the database based on it's ID
pub fn (mut client OurDBClient) get(id u32) !KeyValueData {
req := httpconnection.Request{
prefix: 'get/${id}'
method: .get
}
mut http := client.connection()!
response := http.get_json(req)!
mut decoded_response := json.decode(Response[KeyValueData], response)!
return decoded_response.data
}
// Deletes a value in the database based on it's ID
pub fn (mut client OurDBClient) delete(id u32) ! {
req := httpconnection.Request{
prefix: 'delete/${id}'
method: .delete
}
mut http := client.connection()!
http.delete(req) or { return error('Failed to delete key due to: ${err}') }
}

View File

@@ -136,8 +136,8 @@ fn (server OurDBServer) success[T](args SuccessResponse[T]) SuccessResponse[T] {
} }
// Request body structure for the `/set` endpoint // Request body structure for the `/set` endpoint
struct SetRequestBody { pub struct KeyValueData {
mut: pub mut:
id u32 // Record ID id u32 // Record ID
value string // Value to store value string // Value to store
} }
@@ -146,7 +146,7 @@ mut:
@['/set'; post] @['/set'; post]
pub fn (mut server OurDBServer) set(mut ctx ServerContext) veb.Result { pub fn (mut server OurDBServer) set(mut ctx ServerContext) veb.Result {
request_body := ctx.req.data.str() request_body := ctx.req.data.str()
mut decoded_body := json.decode(SetRequestBody, request_body) or { mut decoded_body := json.decode(KeyValueData, request_body) or {
ctx.res.set_status(.bad_request) ctx.res.set_status(.bad_request)
return ctx.json[ErrorResponse](server.error( return ctx.json[ErrorResponse](server.error(
error: 'bad_request' error: 'bad_request'
@@ -197,7 +197,7 @@ pub fn (mut server OurDBServer) get(mut ctx ServerContext, id string) veb.Result
)) ))
} }
data := SetRequestBody{ data := KeyValueData{
id: id_ id: id_
value: record.bytestr() value: record.bytestr()
} }