Skip to content
This repository has been archived by the owner on May 3, 2024. It is now read-only.

Commit

Permalink
go/mio: add an example of REST API server
Browse files Browse the repository at this point in the history
Signed-off-by: kiwionly2 <gheewooi.ong@seagate.com>
  • Loading branch information
kiwionly2 committed Oct 20, 2022
1 parent 1981fb0 commit e07a5a2
Show file tree
Hide file tree
Showing 12 changed files with 858 additions and 0 deletions.
67 changes: 67 additions & 0 deletions bindings/go/go_motr_api_server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@

## Motr REST API server

Motr REST API Server is a simple REST API Server which based on motr go bindings.

It included a REST API server and a http client.

The API implemented as below:

```
kv/put
kv/get
kv/delete
object/read
object/write
```

#### Server

To start REST API server, first you need to run motr server.

Follow motr [quick start guide](/doc/Quick-Start-Guide.rst) to build motr.

To start motr in development mode, open a new terminal, go to `%cortx_motr_dir%/motr/examples` at `motr` root directory.

```sh
cd %cortx_home%/motr/examples
./setup_a_running_motr_system.sh
```

After motr server start, you should see the connection information at the end of terminal.

At this directory, open `motr.toml`, this is where `motr` connection configure, modify the value accordingly and save the file.

Open another terminal and run the REST API server :
```
> go mod tidy
> export MOTR_HOME=motr_root_diretory
> CGO_CFLAGS="-I/$MOTR_HOME -I/usr/include/motr -DM0_EXTERN=extern -DM0_INTERNAL= -Wno-attributes" CGO_LDFLAGS="-L$MOTR_HOME/motr/.libs -Wl,-rpath=$MOTR_HOME/motr/.libs -lmotr" go run .
```

You should see the server start and running at port `8081`.


#### Client

To interect with the REST API server, you could use http client under `http_client` folder:

```go
client := NewClient("localhost", 8081)

// read from file and write to motr
response, err := client.Write(IndexID, "black_hole.png")

// read from motr and write to file
err := client.Read(IndexID, objectSize, "out.png")
```

Open another terminal and run the tests to see if the http client work as expected.
```
> cd http_client
> go test -v
```

You could refer to test cases for more sample of client usage.


108 changes: 108 additions & 0 deletions bindings/go/go_motr_api_server/api/api_common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package api

import (
"errors"
"fmt"
"io/ioutil"
"net/url"
"strconv"
"time"

"github.com/gin-gonic/gin"
)

func get(ctx *gin.Context, name string, required bool, defaultValue interface{}, errs *[]error) interface{} {

var val string

if ctx.Request.Method == "POST" {
val = ctx.PostForm(name)

// if POST binary content, get from query
if val == "" {
val = ctx.Query(name)
}

} else {
val = ctx.Query(name)
}

if required && val == "" {
toError(errs, name+" is required")
return defaultValue
} else if val == "" {
return defaultValue
}

val, err := url.QueryUnescape(val)

if err != nil {
toError(errs, err.Error())
}

return val
}

func getString(ctx *gin.Context, name string, required bool, defaultValue interface{}, errs *[]error) string {
val := get(ctx, name, required, defaultValue, errs)
return toString(val)
}

func getInt(ctx *gin.Context, name string, required bool, defaultValue interface{}, errs *[]error) int {
val := get(ctx, name, required, defaultValue, errs)
c := toI32(val)
return int(c)
}

func getBytes(ctx *gin.Context, errs *[]error) []byte {
ByteBody, err := ioutil.ReadAll(ctx.Request.Body)

if err != nil {
toError(errs, err.Error())
return nil
}

return ByteBody
}

func toError(errs *[]error, msg string) {
*errs = append(*errs, errors.New(msg))
}

func hasError(errs []error) error {

for _, err := range errs {
if err != nil {
return err
}
}

return nil
}

func createError(err error) map[string]interface{} {

errs := make(map[string]interface{})
errs["error"] = err.Error()

return errs
}

func toTime(end time.Duration) string {
return fmt.Sprintf("%v", end)
}

func toString(t interface{}) string {
return fmt.Sprintf("%+v", t)
}

func toI32(t interface{}) int32 {
s := toString(t)
i, err := strconv.Atoi(s)

if err != nil {
return 0
}

return int32(i)
}
125 changes: 125 additions & 0 deletions bindings/go/go_motr_api_server/api/kv_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package api

import (
"fmt"
"go_motr_api_server/v2/motr"
"net/http"
"time"

"github.com/gin-gonic/gin"
)

func Status(ctx *gin.Context) {

json := make(map[string]interface{})
json["status"] = "ok"

ctx.JSON(http.StatusOK, json)
}

func GetKey(ctx *gin.Context) {

start := time.Now()

errs := &[]error{}

indexID := getString(ctx, "index_id", true, nil, errs)
key := getString(ctx, "key", true, nil, errs)

if err := hasError(*errs); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, createError(err))
return
}

value, err := motr.Get(indexID, key)

if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, createError(err))
return
}

end := time.Since(start)

meta := make(map[string]interface{})
meta["time"] = toTime(end)

json := make(map[string]interface{})
json["meta"] = meta
json["index_id"] = indexID
json["key"] = key
json["value"] = value

ctx.JSON(http.StatusOK, json)
}

func DeleteKey(ctx *gin.Context) {

start := time.Now()

errs := &[]error{}

indexID := getString(ctx, "index_id", true, nil, errs)
key := getString(ctx, "key", true, nil, errs)

if err := hasError(*errs); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, createError(err))
return
}

err := motr.Delete(indexID, key)

if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, createError(err))
return
}

end := time.Since(start)

meta := make(map[string]interface{})
meta["time"] = toTime(end)

json := make(map[string]interface{})
json["meta"] = meta
json["index_id"] = indexID
json["key"] = key

ctx.JSON(http.StatusOK, json)
}

func PutKeyValue(ctx *gin.Context) {

start := time.Now()

errs := &[]error{}

indexID := getString(ctx, "index_id", true, nil, errs)
key := getString(ctx, "key", true, nil, errs)
value := getString(ctx, "value", true, nil, errs)

if err := hasError(*errs); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, createError(err))
return
}

fmt.Println(indexID, key, value)

err := motr.Put(indexID, key, value)

if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, createError(err))
return
}

end := time.Since(start)

meta := make(map[string]interface{})
meta["time"] = toTime(end)

json := make(map[string]interface{})
json["meta"] = meta
json["index_id"] = indexID
json["key"] = key
json["value"] = value

ctx.JSON(http.StatusOK, json)
}
72 changes: 72 additions & 0 deletions bindings/go/go_motr_api_server/api/object_api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package api

import (
"bytes"
"go_motr_api_server/v2/motr"
"net/http"
"time"

"github.com/gin-gonic/gin"
)

func Write(ctx *gin.Context) {

start := time.Now()

errs := &[]error{}

indexID := getString(ctx, "index_id", true, nil, errs)
size := getInt(ctx, "size", true, nil, errs)
byteBody := getBytes(ctx, errs)

if err := hasError(*errs); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, createError(err))
return
}

err := motr.Write("", indexID, uint64(size), bytes.NewReader(byteBody))

if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, createError(err))
return
}

end := time.Since(start)

meta := make(map[string]interface{})
meta["time"] = toTime(end)

json := make(map[string]interface{})
json["meta"] = meta
json["indexID"] = indexID
json["size"] = size

ctx.JSON(http.StatusOK, json)
}

func Read(ctx *gin.Context) {

start := time.Now()

errs := &[]error{}

indexID := getString(ctx, "index_id", true, nil, errs)
size := getInt(ctx, "size", true, nil, errs)

if err := hasError(*errs); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, createError(err))
return
}

byteContent, err := motr.Read("", indexID, uint64(size))

if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, createError(err))
return
}

end := time.Since(start)

ctx.Header("time", toTime(end))
ctx.Data(http.StatusOK, "application/octet-stream", byteContent)
}
Loading

0 comments on commit e07a5a2

Please sign in to comment.