"time"
"github.com/gin-gonic/gin"
+ "github.com/iotbzh/xds-agent/lib/apiv1"
"github.com/iotbzh/xds-agent/lib/xdsconfig"
common "github.com/iotbzh/xds-common/golib"
uuid "github.com/satori/go.uuid"
type XdsFolderType string
const (
- XdsTypePathMap = "PathMap"
+ // XdsTypePathMap Path Mapping folder type
+ XdsTypePathMap = "PathMap"
+ // XdsTypeCloudSync Cloud synchronization (AKA syncthing) folder type
XdsTypeCloudSync = "CloudSync"
- XdsTypeCifsSmb = "CIFS"
+ // XdsTypeCifsSmb CIFS (AKA samba) folder type
+ XdsTypeCifsSmb = "CIFS"
)
// XdsFolderConfig XdsServer folder config
Status string `json:"status"`
IsInSync bool `json:"isInSync"`
DefaultSdk string `json:"defaultSdk"`
+ ClientData string `json:"clientData"` // free form field that can used by client
+
// Specific data depending on which Type is used
DataPathMap XdsPathMapConfig `json:"dataPathMap,omitempty"`
DataCloudSync XdsCloudSyncConfig `json:"dataCloudSync,omitempty"`
// XdsPathMapConfig Path mapping specific data
type XdsPathMapConfig struct {
- ServerPath string `json:"serverPath"`
+ ServerPath string `json:"serverPath"`
+ CheckFile string `json:"checkFile"`
+ CheckContent string `json:"checkContent"`
}
// XdsCloudSyncConfig CloudSync (AKA Syncthing) specific data
Folder XdsFolderConfig `json:"folder"`
}
+// EventCB Event emitter callback
+type EventCB func(privData interface{}, evtData interface{}) error
+
// caller Used to chain event listeners
type caller struct {
- id uuid.UUID
- EventName string
- Func func(interface{})
+ id uuid.UUID
+ EventName string
+ Func EventCB
+ PrivateData interface{}
}
const _IDTempoPrefix = "tempo-"
// GetVersion Send Get request to retrieve XDS Server version
func (xs *XdsServer) GetVersion(res interface{}) error {
- return xs._HTTPGet("/version", &res)
+ return xs.client.Get("/version", &res)
}
// GetFolders Send GET request to get current folder configuration
-func (xs *XdsServer) GetFolders(prjs *[]XdsFolderConfig) error {
- return xs._HTTPGet("/folders", prjs)
+func (xs *XdsServer) GetFolders(folders *[]XdsFolderConfig) error {
+ return xs.client.Get("/folders", folders)
}
// FolderAdd Send POST request to add a folder
-func (xs *XdsServer) FolderAdd(prj *XdsFolderConfig, res interface{}) error {
- response, err := xs._HTTPPost("/folder", prj)
+func (xs *XdsServer) FolderAdd(fld *XdsFolderConfig, res interface{}) error {
+ err := xs.client.Post("/folders", fld, res)
if err != nil {
- return err
+ return fmt.Errorf("FolderAdd error: %s", err.Error())
}
- if response.StatusCode != 200 {
- return fmt.Errorf("FolderAdd error status=%s", response.Status)
- }
- // Result is a XdsFolderConfig that is equivalent to ProjectConfig
- err = json.Unmarshal(xs.client.ResponseToBArray(response), res)
-
return err
}
// FolderDelete Send DELETE request to delete a folder
func (xs *XdsServer) FolderDelete(id string) error {
- return xs.client.HTTPDelete("/folder/" + id)
+ return xs.client.HTTPDelete("/folders/" + id)
}
// FolderSync Send POST request to force synchronization of a folder
func (xs *XdsServer) FolderSync(id string) error {
- return xs.client.HTTPPost("/folder/sync/"+id, "")
+ return xs.client.HTTPPost("/folders/sync/"+id, "")
+}
+
+// FolderUpdate Send PUT request to update a folder
+func (xs *XdsServer) FolderUpdate(fld *XdsFolderConfig, resFld *XdsFolderConfig) error {
+ return xs.client.Put("/folders/"+fld.ID, fld, resFld)
}
// SetAPIRouterGroup .
xs.apiRouter.GET(url, func(c *gin.Context) {
var data interface{}
- if err := xs._HTTPGet(url, &data); err != nil {
+ // Take care of param (eg. id in /projects/:id)
+ nURL := url
+ if strings.Contains(url, ":") {
+ nURL = strings.TrimPrefix(c.Request.URL.Path, xs.APIURL)
+ }
+ // Send Get request
+ if err := xs.client.Get(nURL, &data); err != nil {
if strings.Contains(err.Error(), "connection refused") {
xs.Connected = false
xs._NotifyState()
return
}
- response, err := xs._HTTPPost(url, bodyReq[:n])
+ // Take care of param (eg. id in /projects/:id)
+ nURL := url
+ if strings.Contains(url, ":") {
+ nURL = strings.TrimPrefix(c.Request.URL.Path, xs.APIURL)
+ }
+
+ // Send Post request
+ body, err := json.Marshal(bodyReq[:n])
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
+
+ response, err := xs.client.HTTPPostWithRes(nURL, string(body))
if err != nil {
common.APIError(c, err.Error())
return
}
+
bodyRes, err := ioutil.ReadAll(response.Body)
if err != nil {
common.APIError(c, "Cannot read response body")
// EventRegister Post a request to register to an XdsServer event
func (xs *XdsServer) EventRegister(evName string, id string) error {
- var err error
- _, err = xs._HTTPPost("/events/register", XdsEventRegisterArgs{
- Name: evName,
- ProjectID: id,
- })
- return err
+ return xs.client.Post(
+ "/events/register",
+ XdsEventRegisterArgs{
+ Name: evName,
+ ProjectID: id,
+ },
+ nil)
}
// EventOn Register a callback on events reception
-func (xs *XdsServer) EventOn(evName string, f func(interface{})) (uuid.UUID, error) {
+func (xs *XdsServer) EventOn(evName string, privData interface{}, f EventCB) (uuid.UUID, error) {
if xs.ioSock == nil {
return uuid.Nil, fmt.Errorf("Io.Socket not initialized")
}
// FIXME: use generic type: data interface{} instead of data XdsEventFolderChange
var err error
- if evName == "event:FolderStateChanged" {
+ if evName == "event:folder-state-change" {
err = xs.ioSock.On(evn, func(data XdsEventFolderChange) error {
xs.sockEventsLock.Lock()
- defer xs.sockEventsLock.Unlock()
- for _, c := range xs.sockEvents[evn] {
- c.Func(data)
+ sEvts := make([]*caller, len(xs.sockEvents[evn]))
+ copy(sEvts, xs.sockEvents[evn])
+ xs.sockEventsLock.Unlock()
+ for _, c := range sEvts {
+ c.Func(c.PrivateData, data)
}
return nil
})
} else {
- err = xs.ioSock.On(evn, f)
+ err = xs.ioSock.On(evn, func(data interface{}) error {
+ xs.sockEventsLock.Lock()
+ sEvts := make([]*caller, len(xs.sockEvents[evn]))
+ copy(sEvts, xs.sockEvents[evn])
+ xs.sockEventsLock.Unlock()
+ for _, c := range sEvts {
+ c.Func(c.PrivateData, data)
+ }
+ return nil
+ })
}
if err != nil {
return uuid.Nil, err
}
c := &caller{
- id: uuid.NewV1(),
- EventName: evName,
- Func: f,
+ id: uuid.NewV1(),
+ EventName: evName,
+ Func: f,
+ PrivateData: privData,
}
xs.sockEvents[evName] = append(xs.sockEvents[evName], c)
}
// ProjectToFolder Convert Project structure to Folder structure
-func (xs *XdsServer) ProjectToFolder(pPrj ProjectConfig) *XdsFolderConfig {
+func (xs *XdsServer) ProjectToFolder(pPrj apiv1.ProjectConfig) *XdsFolderConfig {
stID := ""
if pPrj.Type == XdsTypeCloudSync {
stID, _ = xs.SThg.IDGet()
}
+ // TODO: limit ClientData size and gzip it (see https://golang.org/pkg/compress/gzip/)
fPrj := XdsFolderConfig{
ID: pPrj.ID,
Label: pPrj.Label,
Status: pPrj.Status,
IsInSync: pPrj.IsInSync,
DefaultSdk: pPrj.DefaultSdk,
+ ClientData: pPrj.ClientData,
DataPathMap: XdsPathMapConfig{
ServerPath: pPrj.ServerPath,
},
}
// FolderToProject Convert Folder structure to Project structure
-func (xs *XdsServer) FolderToProject(fPrj XdsFolderConfig) ProjectConfig {
+func (xs *XdsServer) FolderToProject(fPrj XdsFolderConfig) apiv1.ProjectConfig {
inSync := fPrj.IsInSync
sts := fPrj.Status
sts = fPrj.DataCloudSync.STSvrStatus
switch fPrj.DataCloudSync.STLocStatus {
- case StatusErrorConfig, StatusDisable, StatusPause:
+ case apiv1.StatusErrorConfig, apiv1.StatusDisable, apiv1.StatusPause:
sts = fPrj.DataCloudSync.STLocStatus
break
- case StatusSyncing:
- if sts != StatusErrorConfig && sts != StatusDisable && sts != StatusPause {
- sts = StatusSyncing
+ case apiv1.StatusSyncing:
+ if sts != apiv1.StatusErrorConfig && sts != apiv1.StatusDisable && sts != apiv1.StatusPause {
+ sts = apiv1.StatusSyncing
}
break
- case StatusEnable:
+ case apiv1.StatusEnable:
// keep STSvrStatus
break
}
}
- pPrj := ProjectConfig{
+ pPrj := apiv1.ProjectConfig{
ID: fPrj.ID,
ServerID: xs.ID,
Label: fPrj.Label,
ClientPath: fPrj.ClientPath,
ServerPath: fPrj.DataPathMap.ServerPath,
- Type: ProjectType(fPrj.Type),
+ Type: apiv1.ProjectType(fPrj.Type),
Status: sts,
IsInSync: inSync,
DefaultSdk: fPrj.DefaultSdk,
+ ClientData: fPrj.ClientData,
}
return pPrj
}
return nil
}
-// _HTTPGet .
-func (xs *XdsServer) _HTTPGet(url string, data interface{}) error {
- var dd []byte
- if err := xs.client.HTTPGet(url, &dd); err != nil {
- return err
- }
- return json.Unmarshal(dd, &data)
-}
-
-// _HTTPPost .
-func (xs *XdsServer) _HTTPPost(url string, data interface{}) (*http.Response, error) {
- body, err := json.Marshal(data)
- if err != nil {
- return nil, err
- }
- return xs.client.HTTPPostWithRes(url, string(body))
-}
-
// Re-established connection
func (xs *XdsServer) _reconnect() error {
err := xs._connect(true)
func (xs *XdsServer) _connect(reConn bool) error {
xdsCfg := XdsServerConfig{}
- if err := xs._HTTPGet("/config", &xdsCfg); err != nil {
+ if err := xs.client.Get("/config", &xdsCfg); err != nil {
xs.Connected = false
if !reConn {
xs._NotifyState()
// Send event to notify changes
func (xs *XdsServer) _NotifyState() {
- evSts := ServerCfg{
+ evSts := apiv1.ServerCfg{
ID: xs.ID,
URL: xs.BaseURL,
APIURL: xs.APIURL,
ConnRetry: xs.ConnRetry,
Connected: xs.Connected,
}
- if err := xs.events.Emit(EVTServerConfig, evSts); err != nil {
+ if err := xs.events.Emit(apiv1.EVTServerConfig, evSts, ""); err != nil {
xs.Log.Warningf("Cannot notify XdsServer state change: %v", err)
}
}