Skip to content

Commit

Permalink
feat: Support importer APIs (#33)
Browse files Browse the repository at this point in the history
* Supports nebula-importer apis and add a custom logger

* feat: Support importer timecost stat and remove chores

* Add importer upload files support

* Modify the logging format

* Fix 404 routers

* Remove redeclared struct

* Migrate import and taskmgr into importer package

* Replace nebula client logger with httpgateway logger adapter

* Make filepath only be absolute

* Update README.md

* Optimize file create prems

* Update README

* Supports set import config body in Request Body

* Handle config body unmarshal error

* Update README

* Update README

* echo the task id after import && support query task detail status

* Update README

* remove chore dev logging

* Add task not start err handle in

* Support save task status to sqlite3

* remove

* fix query action bug

* use channel to wait server quit singal to make shutdown slowly

* add code comments and optimize import actions

Co-authored-by: Yee <[email protected]>
  • Loading branch information
freekatz and yixinglu authored Oct 8, 2021
1 parent 7874667 commit 116d7f3
Show file tree
Hide file tree
Showing 14 changed files with 867 additions and 4 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ nebula-httpd
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
.idea
.vscode/
vendor/

# Dependency directories (remove the comment below to include it)
tmp/

err/
logs/
uploads/
*.db
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,81 @@ response:
"message": "Disconnect successfully"
}
```

#### Import API ####

The requested json body

```json
{
"configPath": "examples/v2/example.yaml"
}
```

The description of the parameters is as follows.

| Field | Description |
| ---------- | ------------------------------------------------------------ |
| configPath | `configPath` is a relative path that under the `uploadspath` in `app.conf`. |
| configBody | `configBody` is the detail configuration with JSON format (instead of YAML format).|

If you choose to use `configPath`, you need to make sure that the config file has been uploaded to `uploadspath`.

```bash
$ curl -X POST -d '{"configPath": "./examples/v2/example.yaml","configBody": {}}' http://127.0.0.1:8080/api/task/import
```

If you choose to use `configBody`, you need to set the `configPath` value to `""` and set the `configBody` as JSON format.

response:

```json
{
"code": 0,
"data": [
"1"
],
"message": "Import task 1 submit successfully"
}
```

#### Action API ####

The requested json body

```json
{
"taskID": "1",
"taskAction": "actionQuery"
}
```

The description of the parameters is as follows.

| Field | Description |
| ---------- | ---------------------------------------------------- |
| taskID | Set the task id to do task action |
| taskAction | The task action enums, include: actionQuery, actionQueryAll, actionStop, actionStopAll, etc. |

```bash
$ curl -X POST -d '{"taskID": "1", "taskAction": "actionQuery"}' http://127.0.0.1:8080/api/task/import/action
```

response:

```json
{
"code": 0,
"data": {
"results": [
{
"taskID": "1",
"taskStatus": "statusProcessing"
}
],
"msg": "Task is processing"
},
"message": "Processing a task action successfully"
}
```

31 changes: 31 additions & 0 deletions common/tools.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package common

import (
"errors"
"os"
"path"
"path/filepath"
"strconv"
"syscall"
)

func CreateFileWithPerm(filePath string, permCode string) (*os.File, error) {

if abs := filepath.IsAbs(filePath); !abs {
return nil, errors.New("file path should be absolute path")
}

perm, err := strconv.ParseInt(permCode, 8, 64)
if err != nil {
return nil, err
}
mask := syscall.Umask(0)
defer syscall.Umask(mask)
filedir := path.Dir(filePath)
os.MkdirAll(filedir, os.FileMode(perm))
fd, err := os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(perm))
if os.IsExist(err) {
os.Chmod(filePath, os.FileMode(perm))
}
return fd, err
}
3 changes: 3 additions & 0 deletions conf/app.conf
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ httpaddr = 0.0.0.0
runmode = dev
autorender = false
copyrequestbody = true
logspath = "./logs/"
uploadspath = "./uploads/"
sqlitedbfilepath = "./tasks.db"
79 changes: 79 additions & 0 deletions controllers/task.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package controllers

import (
"encoding/json"
"fmt"

"github.com/astaxie/beego"
"github.com/vesoft-inc/nebula-http-gateway/service/importer"
"github.com/vesoft-inc/nebula-importer/pkg/config"

importerErrors "github.com/vesoft-inc/nebula-importer/pkg/errors"
)

type TaskController struct {
beego.Controller
}

type ImportRequest struct {
ConfigPath string `json:"configPath"`
ConfigBody config.YAMLConfig `json:"configBody"`
}

type ImportActionRequest struct {
TaskID string `json:"taskID"`
TaskAction string `json:"taskAction"`
}

func (this *TaskController) Import() {
var (
res Response
params ImportRequest
taskID string = importer.NewTaskID()
err error
)

task := importer.NewTask(taskID)
importer.GetTaskMgr().PutTask(taskID, &task)

err = json.Unmarshal(this.Ctx.Input.RequestBody, &params)

if err != nil {
err = importerErrors.Wrap(importerErrors.InvalidConfigPathOrFormat, err)
} else {
err = importer.Import(taskID, params.ConfigPath, &params.ConfigBody)
}

if err != nil {
// task err: import task not start err handle
task.TaskStatus = importer.StatusAborted.String()
beego.Error(fmt.Sprintf("Failed to start a import task: `%s`, task result: `%v`", taskID, err))

res.Code = -1
res.Message = err.Error()
} else {
res.Code = 0
res.Data = []string{taskID}
res.Message = fmt.Sprintf("Import task %s submit successfully", taskID)
}
this.Data["json"] = &res
this.ServeJSON()
}

func (this *TaskController) ImportAction() {
var res Response
var params ImportActionRequest

json.Unmarshal(this.Ctx.Input.RequestBody, &params)
result, err := importer.ImportAction(params.TaskID, importer.NewTaskAction(params.TaskAction))
if err == nil {
res.Code = 0
res.Data = result
res.Message = "Processing a task action successfully"
} else {
res.Code = -1
res.Message = err.Error()
}
this.Data["json"] = &res
this.ServeJSON()
}
107 changes: 107 additions & 0 deletions controllers/task_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package controllers

import (
"bytes"
"encoding/json"
"io/ioutil"
"log"
"net/http"
"testing"
)

func Test_Task_Import(t *testing.T) {
/*
*/
cases := []struct {
path string
requestMethod string
requestBody []byte
}{
{
"http://127.0.0.1:8080/api/task/import",
"POST",
[]byte(`{"configPath" : "examples/v2/example.yaml"}`),
},
}
for _, tc := range cases {
var Response Response
req, err := http.NewRequest(tc.requestMethod, tc.path, bytes.NewBuffer(tc.requestBody))
req.Header.Set("Content-Type", "application/json")

if err != nil {
log.Fatal(err)
}

client := &http.Client{}
resp, err := client.Do(req)

if err != nil {
log.Fatal(err)
}

defer resp.Body.Close()

body, _ := ioutil.ReadAll(resp.Body)

json.Unmarshal([]byte(body), &Response)
if Response.Code != -1 && Response.Code != 0 {
t.Fail()
}
}
}

func Test_Task_Action(t *testing.T) {
/*
*/
cases := []struct {
path string
requestMethod string
requestBody []byte
}{
{
"http://127.0.0.1:8080/api/task/action",
"POST",
[]byte(`{"taskID" : "0", "taskAction": "stop"}`),
},
{
"http://127.0.0.1:8080/api/task/action",
"POST",
[]byte(`{"taskAction": "stopAll"}`),
},
{
"http://127.0.0.1:8080/api/task/action",
"POST",
[]byte(`{"taskID" : "0", "taskAction": "query"}`),
},
{
"http://127.0.0.1:8080/api/task/action",
"POST",
[]byte(`{"taskAction": "queryAll"}`),
},
}
for _, tc := range cases {
var Response Response
req, err := http.NewRequest(tc.requestMethod, tc.path, bytes.NewBuffer(tc.requestBody))
req.Header.Set("Content-Type", "application/json")

if err != nil {
log.Fatal(err)
}

client := &http.Client{}
resp, err := client.Do(req)

if err != nil {
log.Fatal(err)
}

defer resp.Body.Close()

body, _ := ioutil.ReadAll(resp.Body)

json.Unmarshal([]byte(body), &Response)
if Response.Code != -1 && Response.Code != 0 {
t.Fail()
}
}
}
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ require (
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
github.com/facebook/fbthrift v0.31.1-0.20210223140454-614a73a42488
github.com/google/go-cmp v0.5.4 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/prometheus/client_golang v1.9.0 // indirect
github.com/satori/go.uuid v1.2.0
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
github.com/stretchr/testify v1.7.0 // indirect
github.com/vesoft-inc/nebula-go/v2 v2.5.0
github.com/vesoft-inc/nebula-importer v0.0.0-20210716031041-8882282482b2
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 // indirect
golang.org/x/text v0.3.4 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.25.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
Expand Down Expand Up @@ -309,6 +310,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
Expand All @@ -319,6 +321,8 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijb
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vesoft-inc/nebula-go/v2 v2.5.0 h1:A4wZoGqR1W1mQ9y/X+tV1wCMIsDkuhFMthTQKMeT8Yc=
github.com/vesoft-inc/nebula-go/v2 v2.5.0/go.mod h1:fehDUs97/mpmxXi9WezhznX0Dg7hmQRUoOWgDZv9zG0=
github.com/vesoft-inc/nebula-importer v0.0.0-20210716031041-8882282482b2 h1:TJnK8RiJeFxzgo37iAC4roGeotieejr/EEryiaSSSo8=
github.com/vesoft-inc/nebula-importer v0.0.0-20210716031041-8882282482b2/go.mod h1:kHxOwdaHWgpypNvGK/biKn4+mVWT28o51sGmZ1YdOjA=
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
Expand Down
Loading

0 comments on commit 116d7f3

Please sign in to comment.