Add folder update support and ClientData field.
authorSebastien Douheret <sebastien.douheret@iot.bzh>
Fri, 24 Nov 2017 00:28:00 +0000 (01:28 +0100)
committerSebastien Douheret <sebastien.douheret@iot.bzh>
Fri, 24 Nov 2017 00:28:00 +0000 (01:28 +0100)
- folder config can be updated using PUT /folders/:id route
- ClientData field of FolderConfig can be used by client to store any data (used from example by dashboard to save build settings)

.vscode/settings.json
glide.yaml
lib/apiv1/apiv1.go
lib/apiv1/events.go
lib/apiv1/folders.go
lib/folder/folder-interface.go
lib/folder/folder-pathmap.go
lib/folder/folder-st-disable.go
lib/model/folders.go
lib/syncthing/folder-st.go

index d2c6144..569ca52 100644 (file)
@@ -1,61 +1,33 @@
 // Place your settings in this file to overwrite default and user settings.
 {
-  // Configure glob patterns for excluding files and folders.
-  "files.exclude": {
-    ".tmp": true,
-    ".git": true,
-    "glide.lock": true,
-    "vendor": true,
-    "debug": true,
-    "bin": true,
-    "tools": true,
-    "webapp/dist": true,
-    "webapp/node_modules": true
-  },
-  // Specify paths/files to ignore. (Supports Globs)
-  "cSpell.ignorePaths": [
-    "**/node_modules/**",
-    "**/vscode-extension/**",
-    "**/.git/**",
-    "**/vendor/**",
-    ".vscode",
-    "typings"
-  ],
-  // Words to add to dictionary for a workspace.
-  "cSpell.words": [
-    "apiv",
-    "gonic",
-    "devel",
-    "csrffound",
-    "Syncthing",
-    "STID",
-    "ISTCONFIG",
-    "socketio",
-    "ldflags",
-    "SThg",
-    "Intf",
-    "dismissible",
-    "rpath",
-    "WSID",
-    "sess",
-    "IXDS",
-    "xdsconfig",
-    "xdsserver",
-    "mfolder",
-    "inotify",
-    "Inot",
-    "pname",
-    "pkill",
-    "sdkid",
-    "CLOUDSYNC",
-    "xdsagent",
-    "gdbserver",
-    "golib",
-    "eows",
-    "mfolders",
-    "IFOLDER",
-    "flds",
-    "dflt",
-    "stconfig"
-  ]
+    // Configure glob patterns for excluding files and folders.
+    "files.exclude": {
+        ".tmp": true,
+        ".git": true,
+        "glide.lock": true,
+        "vendor": true,
+        "debug": true,
+        "bin": true,
+        "tools": true,
+        "webapp/dist": true,
+        "webapp/node_modules": true
+    },
+    // Specify paths/files to ignore. (Supports Globs)
+    "cSpell.ignorePaths": [
+        "**/node_modules/**",
+        "**/vscode-extension/**",
+        "**/.git/**",
+        "**/vendor/**",
+        ".vscode",
+        "typings"
+    ],
+    // Words to add to dictionary for a workspace.
+    "cSpell.words": [
+        "apiv", "gonic", "devel", "csrffound", "Syncthing", "STID",
+        "ISTCONFIG", "socketio", "ldflags", "SThg", "Intf", "dismissible",
+        "rpath", "WSID", "sess", "IXDS", "xdsconfig", "xdsserver", "mfolder",
+        "inotify", "Inot", "pname", "pkill", "sdkid", "CLOUDSYNC", "xdsagent",
+        "gdbserver", "golib", "eows", "mfolders", "IFOLDER", "flds", "dflt",
+        "stconfig", "reflectme", "franciscocpg"
+    ]
 }
index 94ef2db..fcd0f5c 100644 (file)
@@ -31,3 +31,5 @@ import:
   - golib/eows
 - package: github.com/kr/pty
   version: ^1.0.0
+- package: github.com/franciscocpg/reflectme
+  version: ^0.1.9
index d10a08e..fffed2d 100644 (file)
@@ -40,6 +40,7 @@ func New(r *gin.Engine, sess *session.Sessions, cfg *xdsconfig.Config, mfolders
 
        s.apiRouter.GET("/folders", s.getFolders)
        s.apiRouter.GET("/folders/:id", s.getFolder)
+       s.apiRouter.PUT("/folders/:id", s.updateFolder)
        s.apiRouter.POST("/folders", s.addFolder)
        s.apiRouter.POST("/folders/sync/:id", s.syncFolder)
        s.apiRouter.DELETE("/folders/:id", s.delFolder)
index 9444262..d837571 100644 (file)
@@ -2,6 +2,7 @@ package apiv1
 
 import (
        "net/http"
+       "strings"
        "time"
 
        "github.com/iotbzh/xds-server/lib/folder"
@@ -29,8 +30,15 @@ type EventMsg struct {
 }
 
 // EventEvent Event send in WS when an internal event (eg. Syncthing event is received)
-const EventEventAll = "event:all"
-const EventEventType = "event:" // following by event type
+const (
+       // EventTypePrefix Used as event prefix
+       EventTypePrefix = "event:" // following by event type
+
+       // Supported Events type
+       EVTAll               = EventTypePrefix + "all"
+       EVTFolderChange      = EventTypePrefix + "folder-change"       // type EventMsg with Data type apiv1.???
+       EVTFolderStateChange = EventTypePrefix + "folder-state-change" // type EventMsg with Data type apiv1.???
+)
 
 // eventsList Registering for events that will be send over a WS
 func (s *APIService) eventsList(c *gin.Context) {
@@ -52,7 +60,7 @@ func (s *APIService) eventsRegister(c *gin.Context) {
                return
        }
 
-       evType := "FolderStateChanged"
+       evType := strings.TrimPrefix(EVTFolderStateChange, EventTypePrefix)
        if args.Name != evType {
                common.APIError(c, "Unsupported event name")
                return
@@ -79,11 +87,11 @@ func (s *APIService) eventsRegister(c *gin.Context) {
                        Data: ev.Data,
                }
 
-               if err := (*so).Emit(EventEventAll, msg); err != nil {
+               if err := (*so).Emit(EVTAll, msg); err != nil {
                        s.log.Errorf("WS Emit Event : %v", err)
                }
 
-               if err := (*so).Emit(EventEventType+ev.Type, msg); err != nil {
+               if err := (*so).Emit(EventTypePrefix+ev.Type, msg); err != nil {
                        s.log.Errorf("WS Emit Event : %v", err)
                }
        }
@@ -113,9 +121,9 @@ func (s *APIService) eventsRegister(c *gin.Context) {
                }
 
                s.log.Debugf("WS Emit %s - Status=%10s, IsInSync=%6v, ID=%s",
-                       EventEventType+evType, cfg.Status, cfg.IsInSync, cfg.ID)
+                       EventTypePrefix+evType, cfg.Status, cfg.IsInSync, cfg.ID)
 
-               if err := (*so).Emit(EventEventType+evType, msg); err != nil {
+               if err := (*so).Emit(EventTypePrefix+evType, msg); err != nil {
                        s.log.Errorf("WS Emit Folder StateChanged event : %v", err)
                }
        }
index 398e21c..073445c 100644 (file)
@@ -105,3 +105,27 @@ func (s *APIService) delFolder(c *gin.Context) {
        }
        c.JSON(http.StatusOK, delEntry)
 }
+
+// updateFolder update some field of a folder
+func (s *APIService) updateFolder(c *gin.Context) {
+       id, err := s.mfolders.ResolveID(c.Param("id"))
+       if err != nil {
+               common.APIError(c, err.Error())
+               return
+       }
+
+       s.log.Debugln("Update folder id ", id)
+
+       var cfgArg folder.FolderConfig
+       if c.BindJSON(&cfgArg) != nil {
+               common.APIError(c, "Invalid arguments")
+               return
+       }
+
+       upFld, err := s.mfolders.Update(id, cfgArg)
+       if err != nil {
+               common.APIError(c, err.Error())
+               return
+       }
+       c.JSON(http.StatusOK, upFld)
+}
index 9eb6829..3208869 100644 (file)
@@ -30,6 +30,7 @@ type IFOLDER interface {
        ConvPathCli2Svr(s string) string                          // Convert path from Client to Server
        ConvPathSvr2Cli(s string) string                          // Convert path from Server to Client
        Remove() error                                            // Remove a folder
+       Update(cfg FolderConfig) (*FolderConfig, error)           // Update a new folder
        RegisterEventChange(cb *EventCB, data *EventCBData) error // Request events registration (sent through WS)
        UnRegisterEventChange() error                             // Un-register events
        Sync() error                                              // Force folder files synchronization
@@ -45,6 +46,7 @@ type FolderConfig struct {
        Status     string     `json:"status"`
        IsInSync   bool       `json:"isInSync"`
        DefaultSdk string     `json:"defaultSdk"`
+       ClientData string     `json:"clientData"` // free form field that can used by client
 
        // Not exported fields from REST API point of view
        RootPath string `json:"-"`
@@ -58,6 +60,11 @@ type FolderConfig struct {
        DataCloudSync CloudSyncConfig `json:"dataCloudSync,omitempty"`
 }
 
+// FolderConfigUpdatableFields List fields that can be updated using Update function
+var FolderConfigUpdatableFields = []string{
+       "Label", "DefaultSdk", "ClientData",
+}
+
 // PathMapConfig Path mapping specific data
 type PathMapConfig struct {
        ServerPath string `json:"serverPath"`
index e200164..c5691a3 100644 (file)
@@ -145,6 +145,15 @@ func (f *PathMap) Remove() error {
        return nil
 }
 
+// Update update some fields of a folder
+func (f *PathMap) Update(cfg FolderConfig) (*FolderConfig, error) {
+       if f.config.ID != cfg.ID {
+               return nil, fmt.Errorf("Invalid id")
+       }
+       f.config = cfg
+       return &f.config, nil
+}
+
 // RegisterEventChange requests registration for folder change event
 func (f *PathMap) RegisterEventChange(cb *EventCB, data *EventCBData) error {
        return nil
index 7b53ca8..e936494 100644 (file)
@@ -65,6 +65,11 @@ func (f *STFolderDisable) Remove() error {
        return nil
 }
 
+// Update update some fields of a folder
+func (f *STFolderDisable) Update(cfg FolderConfig) (*FolderConfig, error) {
+       return nil, nil
+}
+
 // RegisterEventChange requests registration for folder change event
 func (f *STFolderDisable) RegisterEventChange(cb *EventCB, data *EventCBData) error {
        return nil
index b8e6cf5..0e28538 100644 (file)
@@ -10,6 +10,7 @@ import (
        "time"
 
        "github.com/Sirupsen/logrus"
+       "github.com/franciscocpg/reflectme"
        common "github.com/iotbzh/xds-common/golib"
        "github.com/iotbzh/xds-server/lib/folder"
        "github.com/iotbzh/xds-server/lib/syncthing"
@@ -318,6 +319,54 @@ func (f *Folders) Delete(id string) (folder.FolderConfig, error) {
        return fld, err
 }
 
+// Update Update a specific folder
+func (f *Folders) Update(id string, cfg folder.FolderConfig) (*folder.FolderConfig, error) {
+       fcMutex.Lock()
+       defer fcMutex.Unlock()
+
+       fc, exist := f.folders[id]
+       if !exist {
+               return nil, fmt.Errorf("unknown id")
+       }
+
+       // Copy current in a new object to change nothing in case of an error rises
+       newCfg := folder.FolderConfig{}
+       reflectme.Copy((*fc).GetConfig(), &newCfg)
+
+       // Only update some fields
+       dirty := false
+       for _, fieldName := range folder.FolderConfigUpdatableFields {
+               valNew, err := reflectme.GetField(cfg, fieldName)
+               if err == nil {
+                       valCur, err := reflectme.GetField(newCfg, fieldName)
+                       if err == nil && valNew != valCur {
+                               err = reflectme.SetField(&newCfg, fieldName, valNew)
+                               if err != nil {
+                                       return nil, err
+                               }
+                               dirty = true
+                       }
+               }
+       }
+
+       if !dirty {
+               return &newCfg, nil
+       }
+
+       fld, err := (*fc).Update(newCfg)
+       if err != nil {
+               return fld, err
+       }
+
+       // Save config on disk
+       err = f.SaveConfig()
+
+       // Send event to notified changes
+       // TODO emit folder change event
+
+       return fld, err
+}
+
 // RegisterEventChange requests registration for folder event change
 func (f *Folders) RegisterEventChange(id string, cb *folder.EventCB, data *folder.EventCBData) error {
 
index f25a505..74aa4bb 100644 (file)
@@ -143,6 +143,15 @@ func (f *STFolder) Remove() error {
        return err2
 }
 
+// Update update some fields of a folder
+func (f *STFolder) Update(cfg folder.FolderConfig) (*folder.FolderConfig, error) {
+       if f.fConfig.ID != cfg.ID {
+               return nil, fmt.Errorf("Invalid id")
+       }
+       f.fConfig = cfg
+       return &f.fConfig, nil
+}
+
 // RegisterEventChange requests registration for folder event change
 func (f *STFolder) RegisterEventChange(cb *folder.EventCB, data *folder.EventCBData) error {
        f.eventChangeCB = cb