3
0

Define record import routes

This commit is contained in:
Tomaž Jerman
2019-08-23 14:38:15 +02:00
parent df17ddb819
commit d0647f0f89
5 changed files with 440 additions and 8 deletions

View File

@@ -667,6 +667,68 @@
]
}
},
{
"name": "importInit",
"path": "/import",
"method": "POST",
"title": "Initiate record import session",
"parameters": {
"post": [
{
"name": "upload",
"type": "*multipart.FileHeader",
"required": true,
"title": "File import"
}
]
}
},
{
"name": "importRun",
"path": "/import/{sessionID}",
"method": "PATCH",
"title": "Run record import",
"parameters": {
"path": [
{
"name": "sessionID",
"type": "uint64",
"required": true,
"title": "Import session"
}
],
"post": [
{
"name": "fields",
"type": "json.RawMessage",
"required": true,
"title": "Fields defined by import file"
},
{
"name": "onError",
"type": "string",
"required": true,
"title": "What happens if record fails to import"
}
]
}
},
{
"name": "importProgress",
"path": "/import/{sessionID}",
"method": "GET",
"title": "Get import progress",
"parameters": {
"path": [
{
"name": "sessionID",
"type": "uint64",
"required": true,
"title": "Import session"
}
]
}
},
{
"name": "export",
"path": "/export{filename}.{ext}",

View File

@@ -91,6 +91,68 @@
]
}
},
{
"Name": "importInit",
"Method": "POST",
"Title": "Initiate record import session",
"Path": "/import",
"Parameters": {
"post": [
{
"name": "upload",
"required": true,
"title": "File import",
"type": "*multipart.FileHeader"
}
]
}
},
{
"Name": "importRun",
"Method": "PATCH",
"Title": "Run record import",
"Path": "/import/{sessionID}",
"Parameters": {
"path": [
{
"name": "sessionID",
"required": true,
"title": "Import session",
"type": "uint64"
}
],
"post": [
{
"name": "fields",
"required": true,
"title": "Fields defined by import file",
"type": "json.RawMessage"
},
{
"name": "onError",
"required": true,
"title": "What happens if record fails to import",
"type": "string"
}
]
}
},
{
"Name": "importProgress",
"Method": "GET",
"Title": "Get import progress",
"Path": "/import/{sessionID}",
"Parameters": {
"path": [
{
"name": "sessionID",
"required": true,
"title": "Import session",
"type": "uint64"
}
]
}
},
{
"Name": "export",
"Method": "GET",

View File

@@ -31,6 +31,9 @@ import (
type RecordAPI interface {
Report(context.Context, *request.RecordReport) (interface{}, error)
List(context.Context, *request.RecordList) (interface{}, error)
ImportInit(context.Context, *request.RecordImportInit) (interface{}, error)
ImportRun(context.Context, *request.RecordImportRun) (interface{}, error)
ImportProgress(context.Context, *request.RecordImportProgress) (interface{}, error)
Export(context.Context, *request.RecordExport) (interface{}, error)
Create(context.Context, *request.RecordCreate) (interface{}, error)
Read(context.Context, *request.RecordRead) (interface{}, error)
@@ -41,14 +44,17 @@ type RecordAPI interface {
// HTTP API interface
type Record struct {
Report func(http.ResponseWriter, *http.Request)
List func(http.ResponseWriter, *http.Request)
Export func(http.ResponseWriter, *http.Request)
Create func(http.ResponseWriter, *http.Request)
Read func(http.ResponseWriter, *http.Request)
Update func(http.ResponseWriter, *http.Request)
Delete func(http.ResponseWriter, *http.Request)
Upload func(http.ResponseWriter, *http.Request)
Report func(http.ResponseWriter, *http.Request)
List func(http.ResponseWriter, *http.Request)
ImportInit func(http.ResponseWriter, *http.Request)
ImportRun func(http.ResponseWriter, *http.Request)
ImportProgress func(http.ResponseWriter, *http.Request)
Export func(http.ResponseWriter, *http.Request)
Create func(http.ResponseWriter, *http.Request)
Read func(http.ResponseWriter, *http.Request)
Update func(http.ResponseWriter, *http.Request)
Delete func(http.ResponseWriter, *http.Request)
Upload func(http.ResponseWriter, *http.Request)
}
func NewRecord(h RecordAPI) *Record {
@@ -93,6 +99,66 @@ func NewRecord(h RecordAPI) *Record {
resputil.JSON(w, value)
}
},
ImportInit: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewRecordImportInit()
if err := params.Fill(r); err != nil {
logger.LogParamError("Record.ImportInit", r, err)
resputil.JSON(w, err)
return
}
value, err := h.ImportInit(r.Context(), params)
if err != nil {
logger.LogControllerError("Record.ImportInit", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("Record.ImportInit", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
ImportRun: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewRecordImportRun()
if err := params.Fill(r); err != nil {
logger.LogParamError("Record.ImportRun", r, err)
resputil.JSON(w, err)
return
}
value, err := h.ImportRun(r.Context(), params)
if err != nil {
logger.LogControllerError("Record.ImportRun", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("Record.ImportRun", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
ImportProgress: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewRecordImportProgress()
if err := params.Fill(r); err != nil {
logger.LogParamError("Record.ImportProgress", r, err)
resputil.JSON(w, err)
return
}
value, err := h.ImportProgress(r.Context(), params)
if err != nil {
logger.LogControllerError("Record.ImportProgress", r, err, params.Auditable())
resputil.JSON(w, err)
return
}
logger.LogControllerCall("Record.ImportProgress", r, params.Auditable())
if !serveHTTP(value, w, r) {
resputil.JSON(w, value)
}
},
Export: func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
params := request.NewRecordExport()
@@ -221,6 +287,9 @@ func (h Record) MountRoutes(r chi.Router, middlewares ...func(http.Handler) http
r.Use(middlewares...)
r.Get("/namespace/{namespaceID}/module/{moduleID}/record/report", h.Report)
r.Get("/namespace/{namespaceID}/module/{moduleID}/record/", h.List)
r.Post("/namespace/{namespaceID}/module/{moduleID}/record/import", h.ImportInit)
r.Patch("/namespace/{namespaceID}/module/{moduleID}/record/import/{sessionID}", h.ImportRun)
r.Get("/namespace/{namespaceID}/module/{moduleID}/record/import/{sessionID}", h.ImportProgress)
r.Get("/namespace/{namespaceID}/module/{moduleID}/record/export{filename}.{ext}", h.Export)
r.Post("/namespace/{namespaceID}/module/{moduleID}/record/", h.Create)
r.Get("/namespace/{namespaceID}/module/{moduleID}/record/{recordID}", h.Read)

View File

@@ -175,6 +175,192 @@ func (r *RecordList) Fill(req *http.Request) (err error) {
var _ RequestFiller = NewRecordList()
// Record importInit request parameters
type RecordImportInit struct {
Upload *multipart.FileHeader
NamespaceID uint64 `json:",string"`
ModuleID uint64 `json:",string"`
}
func NewRecordImportInit() *RecordImportInit {
return &RecordImportInit{}
}
func (r RecordImportInit) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["upload.size"] = r.Upload.Size
out["upload.filename"] = r.Upload.Filename
out["namespaceID"] = r.NamespaceID
out["moduleID"] = r.ModuleID
return out
}
func (r *RecordImportInit) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseMultipartForm(32 << 20); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
if _, r.Upload, err = req.FormFile("upload"); err != nil {
return errors.Wrap(err, "error procesing uploaded file")
}
r.NamespaceID = parseUInt64(chi.URLParam(req, "namespaceID"))
r.ModuleID = parseUInt64(chi.URLParam(req, "moduleID"))
return err
}
var _ RequestFiller = NewRecordImportInit()
// Record importRun request parameters
type RecordImportRun struct {
SessionID uint64 `json:",string"`
NamespaceID uint64 `json:",string"`
ModuleID uint64 `json:",string"`
Fields json.RawMessage
OnError string
}
func NewRecordImportRun() *RecordImportRun {
return &RecordImportRun{}
}
func (r RecordImportRun) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["sessionID"] = r.SessionID
out["namespaceID"] = r.NamespaceID
out["moduleID"] = r.ModuleID
out["fields"] = r.Fields
out["onError"] = r.OnError
return out
}
func (r *RecordImportRun) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
r.SessionID = parseUInt64(chi.URLParam(req, "sessionID"))
r.NamespaceID = parseUInt64(chi.URLParam(req, "namespaceID"))
r.ModuleID = parseUInt64(chi.URLParam(req, "moduleID"))
if val, ok := post["fields"]; ok {
r.Fields = json.RawMessage(val)
}
if val, ok := post["onError"]; ok {
r.OnError = val
}
return err
}
var _ RequestFiller = NewRecordImportRun()
// Record importProgress request parameters
type RecordImportProgress struct {
SessionID uint64 `json:",string"`
NamespaceID uint64 `json:",string"`
ModuleID uint64 `json:",string"`
}
func NewRecordImportProgress() *RecordImportProgress {
return &RecordImportProgress{}
}
func (r RecordImportProgress) Auditable() map[string]interface{} {
var out = map[string]interface{}{}
out["sessionID"] = r.SessionID
out["namespaceID"] = r.NamespaceID
out["moduleID"] = r.ModuleID
return out
}
func (r *RecordImportProgress) Fill(req *http.Request) (err error) {
if strings.ToLower(req.Header.Get("content-type")) == "application/json" {
err = json.NewDecoder(req.Body).Decode(r)
switch {
case err == io.EOF:
err = nil
case err != nil:
return errors.Wrap(err, "error parsing http request body")
}
}
if err = req.ParseForm(); err != nil {
return err
}
get := map[string]string{}
post := map[string]string{}
urlQuery := req.URL.Query()
for name, param := range urlQuery {
get[name] = string(param[0])
}
postVars := req.Form
for name, param := range postVars {
post[name] = string(param[0])
}
r.SessionID = parseUInt64(chi.URLParam(req, "sessionID"))
r.NamespaceID = parseUInt64(chi.URLParam(req, "namespaceID"))
r.ModuleID = parseUInt64(chi.URLParam(req, "moduleID"))
return err
}
var _ RequestFiller = NewRecordImportProgress()
// Record export request parameters
type RecordExport struct {
Filter string

View File

@@ -956,6 +956,9 @@ Compose records
| ------ | -------- | ------- |
| `GET` | `/namespace/{namespaceID}/module/{moduleID}/record/report` | Generates report from module records |
| `GET` | `/namespace/{namespaceID}/module/{moduleID}/record/` | List/read records from module section |
| `POST` | `/namespace/{namespaceID}/module/{moduleID}/record/import` | Initiate record import session |
| `PATCH` | `/namespace/{namespaceID}/module/{moduleID}/record/import/{sessionID}` | Run record import |
| `GET` | `/namespace/{namespaceID}/module/{moduleID}/record/import/{sessionID}` | Get import progress |
| `GET` | `/namespace/{namespaceID}/module/{moduleID}/record/export{filename}.{ext}` | Exports records that match |
| `POST` | `/namespace/{namespaceID}/module/{moduleID}/record/` | Create record in module section |
| `GET` | `/namespace/{namespaceID}/module/{moduleID}/record/{recordID}` | Read records by ID from module section |
@@ -1000,6 +1003,56 @@ Compose records
| namespaceID | uint64 | PATH | Namespace ID | N/A | YES |
| moduleID | uint64 | PATH | Module ID | N/A | YES |
## Initiate record import session
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/namespace/{namespaceID}/module/{moduleID}/record/import` | HTTP/S | POST | |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| upload | *multipart.FileHeader | POST | File import | N/A | YES |
| namespaceID | uint64 | PATH | Namespace ID | N/A | YES |
| moduleID | uint64 | PATH | Module ID | N/A | YES |
## Run record import
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/namespace/{namespaceID}/module/{moduleID}/record/import/{sessionID}` | HTTP/S | PATCH | |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| sessionID | uint64 | PATH | Import session | N/A | YES |
| namespaceID | uint64 | PATH | Namespace ID | N/A | YES |
| moduleID | uint64 | PATH | Module ID | N/A | YES |
| fields | json.RawMessage | POST | Fields defined by import file | N/A | YES |
| onError | string | POST | What happens if record fails to import | N/A | YES |
## Get import progress
#### Method
| URI | Protocol | Method | Authentication |
| --- | -------- | ------ | -------------- |
| `/namespace/{namespaceID}/module/{moduleID}/record/import/{sessionID}` | HTTP/S | GET | |
#### Request parameters
| Parameter | Type | Method | Description | Default | Required? |
| --------- | ---- | ------ | ----------- | ------- | --------- |
| sessionID | uint64 | PATH | Import session | N/A | YES |
| namespaceID | uint64 | PATH | Namespace ID | N/A | YES |
| moduleID | uint64 | PATH | Module ID | N/A | YES |
## Exports records that match
#### Method