Fixed Syncthing folder status events and exec command.
[src/xds/xds-agent.git] / lib / agent / project-st.go
index 28a287c..c0d2550 100644 (file)
@@ -1,16 +1,17 @@
 package agent
 
-import "github.com/iotbzh/xds-agent/lib/syncthing"
-
-// SEB TODO
+import (
+       st "github.com/iotbzh/xds-agent/lib/syncthing"
+)
 
 // IPROJECT interface implementation for syncthing projects
 
 // STProject .
 type STProject struct {
        *Context
-       server *XdsServer
-       folder *FolderConfig
+       server   *XdsServer
+       folder   *XdsFolderConfig
+       eventIDs []int
 }
 
 // NewProjectST Create a new instance of STProject
@@ -18,7 +19,7 @@ func NewProjectST(ctx *Context, svr *XdsServer) *STProject {
        p := STProject{
                Context: ctx,
                server:  svr,
-               folder:  &FolderConfig{},
+               folder:  &XdsFolderConfig{},
        }
        return &p
 }
@@ -27,6 +28,7 @@ func NewProjectST(ctx *Context, svr *XdsServer) *STProject {
 func (p *STProject) Add(cfg ProjectConfig) (*ProjectConfig, error) {
        var err error
 
+       // Add project/folder into XDS Server
        err = p.server.FolderAdd(p.server.ProjectToFolder(cfg), p.folder)
        if err != nil {
                return nil, err
@@ -34,19 +36,37 @@ func (p *STProject) Add(cfg ProjectConfig) (*ProjectConfig, error) {
        svrPrj := p.GetProject()
 
        // Declare project into local Syncthing
-       p.SThg.FolderChange(st.FolderChangeArg{
-               ID:           cfg.ID,
-               Label:        cfg.Label,
+       id, err := p.SThg.FolderChange(st.FolderChangeArg{
+               ID:           svrPrj.ID,
+               Label:        svrPrj.Label,
                RelativePath: cfg.ClientPath,
                SyncThingID:  p.server.ServerConfig.Builder.SyncThingID,
        })
+       if err != nil {
+               return nil, err
+       }
 
-       return svrPrj, nil
+       locPrj, err := p.SThg.FolderConfigGet(id)
+       if err != nil {
+               svrPrj.Status = StatusErrorConfig
+               return nil, err
+       }
+       if svrPrj.ID != locPrj.ID {
+               p.Log.Errorf("Project ID in XDSServer and local ST differ: %s != %s", svrPrj.ID, locPrj.ID)
+       }
+
+       // Use Update function to setup remains fields
+       return p.UpdateProject(*svrPrj)
 }
 
 // Delete a project
 func (p *STProject) Delete() error {
-       return p.server.FolderDelete(p.folder.ID)
+       errSvr := p.server.FolderDelete(p.folder.ID)
+       errLoc := p.SThg.FolderDelete(p.folder.ID)
+       if errSvr != nil {
+               return errSvr
+       }
+       return errLoc
 }
 
 // GetProject Get public part of project config
@@ -56,38 +76,111 @@ func (p *STProject) GetProject() *ProjectConfig {
        return &prj
 }
 
-// SetProject Set project config
-func (p *STProject) SetProject(prj ProjectConfig) *ProjectConfig {
-       // SEB TODO
+// UpdateProject Update project config
+func (p *STProject) UpdateProject(prj ProjectConfig) (*ProjectConfig, error) {
+       // Update folder
        p.folder = p.server.ProjectToFolder(prj)
-       return p.GetProject()
+       svrPrj := p.GetProject()
+
+       // Register events to update folder status
+       // Register to XDS Server events
+       p.server.EventOn("event:FolderStateChanged", p._cbServerFolderChanged)
+       if err := p.server.EventRegister("FolderStateChanged", svrPrj.ID); err != nil {
+               p.Log.Warningf("XDS Server EventRegister failed: %v", err)
+               return svrPrj, err
+       }
+
+       // Register to Local Syncthing events
+       for _, evName := range []string{st.EventStateChanged, st.EventFolderPaused} {
+               evID, err := p.SThg.Events.Register(evName, p._cbLocalSTEvents, svrPrj.ID, nil)
+               if err != nil {
+                       return nil, err
+               }
+               p.eventIDs = append(p.eventIDs, evID)
+       }
+
+       return svrPrj, nil
 }
 
 // GetServer Get the XdsServer that holds this project
 func (p *STProject) GetServer() *XdsServer {
-       // SEB TODO
        return p.server
 }
 
-// GetFullPath returns the full path of a directory (from server POV)
-func (p *STProject) GetFullPath(dir string) string {
-       /* SEB
-       if &dir == nil {
-               return p.folder.DataSTProject.ServerPath
-       }
-       return filepath.Join(p.folder.DataSTProject.ServerPath, dir)
-       */
-       return "SEB TODO"
-}
-
 // Sync Force project files synchronization
 func (p *STProject) Sync() error {
-       // SEB TODO
-       return nil
+       if err := p.server.FolderSync(p.folder.ID); err != nil {
+               return err
+       }
+       return p.SThg.FolderScan(p.folder.ID, "")
 }
 
 // IsInSync Check if project files are in-sync
 func (p *STProject) IsInSync() (bool, error) {
-       // SEB TODO
-       return false, nil
+       // Should be up-to-date by callbacks (see below)
+       return p.folder.IsInSync, nil
+}
+
+/**
+** Private functions
+***/
+
+// callback use to update (XDS Server) folder IsInSync status
+
+func (p *STProject) _cbServerFolderChanged(data interface{}) {
+       evt := data.(XdsEventFolderChange)
+
+       // Only process event that concerns this project/folder ID
+       if p.folder.ID != evt.Folder.ID {
+               return
+       }
+
+       if evt.Folder.IsInSync != p.folder.DataCloudSync.STSvrIsInSync ||
+               evt.Folder.Status != p.folder.DataCloudSync.STSvrStatus {
+
+               p.folder.DataCloudSync.STSvrIsInSync = evt.Folder.IsInSync
+               p.folder.DataCloudSync.STSvrStatus = evt.Folder.Status
+
+               if err := p.events.Emit(EVTProjectChange, p.server.FolderToProject(*p.folder)); err != nil {
+                       p.Log.Warningf("Cannot notify project change: %v", err)
+               }
+       }
+}
+
+// callback use to update IsInSync status
+func (p *STProject) _cbLocalSTEvents(ev st.Event, data *st.EventsCBData) {
+
+       inSync := p.folder.DataCloudSync.STLocIsInSync
+       sts := p.folder.DataCloudSync.STLocStatus
+       prevSync := inSync
+       prevStatus := sts
+
+       switch ev.Type {
+
+       case st.EventStateChanged:
+               to := ev.Data["to"]
+               switch to {
+               case "scanning", "syncing":
+                       sts = StatusSyncing
+               case "idle":
+                       sts = StatusEnable
+               }
+               inSync = (to == "idle")
+
+       case st.EventFolderPaused:
+               if sts == StatusEnable {
+                       sts = StatusPause
+               }
+               inSync = false
+       }
+
+       if prevSync != inSync || prevStatus != sts {
+
+               p.folder.DataCloudSync.STLocIsInSync = inSync
+               p.folder.DataCloudSync.STLocStatus = sts
+
+               if err := p.events.Emit(EVTProjectChange, p.server.FolderToProject(*p.folder)); err != nil {
+                       p.Log.Warningf("Cannot notify project change: %v", err)
+               }
+       }
 }