aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastien Douheret <sebastien.douheret@iot.bzh>2017-11-24 01:14:30 +0100
committerSebastien Douheret <sebastien.douheret@iot.bzh>2017-11-24 01:37:24 +0100
commit4d843d2bde236ec23810d0904dfb8aebbc53a37b (patch)
tree84c01452f01620cedb7bf6bcb608a0eade82161b
parent38c0c21a969e621c725245ce91c78e77076c5ce7 (diff)
New dashboard improvements.
- add build buttons - add build settings support and backup into project clientData - improved async alert - fixed project dropdown Signed-off-by: Sebastien Douheret <sebastien.douheret@iot.bzh>
-rw-r--r--.vscode/settings.json52
-rw-r--r--glide.yaml2
-rw-r--r--lib/agent/apiv1-exec.go18
-rw-r--r--lib/agent/apiv1-projects.go28
-rw-r--r--lib/agent/apiv1.go7
-rw-r--r--lib/agent/events.go9
-rw-r--r--lib/agent/project-interface.go15
-rw-r--r--lib/agent/project-pathmap.go22
-rw-r--r--lib/agent/project-st.go33
-rw-r--r--lib/agent/projects.go58
-rw-r--r--lib/agent/sessions.go8
-rw-r--r--lib/agent/xdsserver.go23
-rw-r--r--lib/apiv1/events.go7
-rw-r--r--lib/apiv1/projects.go6
-rw-r--r--webapp/.stylelintrc.json2
-rw-r--r--webapp/src/app/@core-xds/services/alert.service.spec.ts14
-rw-r--r--webapp/src/app/@core-xds/services/alert.service.ts2
-rw-r--r--webapp/src/app/@core-xds/services/build-settings.service.ts78
-rw-r--r--webapp/src/app/@core-xds/services/config.service.spec.ts2
-rw-r--r--webapp/src/app/@core-xds/services/project.service.spec.ts2
-rw-r--r--webapp/src/app/@core-xds/services/project.service.ts189
-rw-r--r--webapp/src/app/@core-xds/services/xds-config.service.ts8
-rw-r--r--webapp/src/app/@core-xds/services/xdsagent.service.ts100
-rw-r--r--webapp/src/app/@theme/components/header/header.component.html35
-rw-r--r--webapp/src/app/pages/build/build-settings-modal/build-settings-modal.component.html60
-rw-r--r--webapp/src/app/pages/build/build-settings-modal/build-settings-modal.component.ts77
-rw-r--r--webapp/src/app/pages/build/build.component.html65
-rw-r--r--webapp/src/app/pages/build/build.component.scss6
-rw-r--r--webapp/src/app/pages/build/build.component.spec.ts2
-rw-r--r--webapp/src/app/pages/build/build.component.ts85
-rw-r--r--webapp/src/app/pages/build/build.module.ts3
-rw-r--r--webapp/src/app/pages/build/settings-modal/build-settings-modal.component.ts143
-rw-r--r--webapp/src/app/pages/build/settings/project-select-dropdown.component.ts25
-rw-r--r--webapp/src/app/pages/config/config-xds/downloadXdsAgent.component.ts5
-rw-r--r--webapp/src/app/pages/config/config.module.ts2
-rw-r--r--webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.html11
-rw-r--r--webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.ts8
-rw-r--r--webapp/src/app/pages/projects/project-card/project-card.component.scss13
-rw-r--r--webapp/src/app/pages/projects/project-card/project-card.component.ts12
-rw-r--r--webapp/src/app/pages/projects/projects.component.html2
-rw-r--r--webapp/src/app/pages/projects/projects.component.scss2
-rw-r--r--webapp/src/app/pages/projects/projects.component.ts2
-rw-r--r--webapp/src/app/pages/projects/projects.module.ts2
-rw-r--r--webapp/src/app/pages/sdks/sdk-card/sdk-card.component.scss13
-rw-r--r--webapp/src/app/pages/sdks/sdk-card/sdk-card.component.ts4
-rw-r--r--webapp/src/app/pages/sdks/sdks.component.html2
-rw-r--r--webapp/src/app/pages/sdks/sdks.component.scss2
-rw-r--r--webapp/src/app/pages/sdks/sdks.component.ts2
-rw-r--r--webapp/src/app/pages/sdks/sdks.module.ts6
49 files changed, 918 insertions, 356 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 4c9ba28..5d43dd0 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -29,51 +29,17 @@
],
// Words to add to dictionary for a workspace.
"cSpell.words": [
- "apiv",
- "gonic",
- "devel",
- "csrffound",
- "Syncthing",
- "STID",
- "ISTCONFIG",
- "socketio",
- "ldflags",
- "SThg",
- "stconfig",
- "Intf",
- "dismissible",
- "rpath",
- "WSID",
- "sess",
- "IXDS",
- "golib",
- "xdsapi",
- "xdsconfig",
- "xdsserver",
- "xdsagent",
- "nbsp",
- "Inot",
- "inotify",
- "cmdi",
- "sdkid",
- "Flds",
- "prjs",
- "iosk",
- "CIFS",
- "IPROJECT",
- "unregister",
- "conv",
- "PATHMAP",
- "nospace",
- "graphx",
- "Truthy",
- "darkviolet",
- "dwnl",
- "topnav",
- "leftbar"
+ "apiv", "gonic", "devel", "csrffound", "Syncthing", "STID", "ISTCONFIG",
+ "socketio", "ldflags", "SThg", "stconfig", "Intf", "dismissible", "rpath",
+ "WSID", "sess", "IXDS", "golib", "xdsapi", "xdsconfig", "xdsserver",
+ "xdsagent", "nbsp", "Inot", "inotify", "cmdi", "sdkid", "Flds", "prjs",
+ "iosk", "CIFS", "IPROJECT", "unregister", "conv", "PATHMAP", "nospace",
+ "graphx", "Truthy", "darkviolet", "dwnl", "topnav", "leftbar", "urfave",
+ "unmarshall", "sebd", "priv", "evts", "gdbserver", "tabset", "pageview",
+ "subpath", "prebuild", "reflectme", "franciscocpg"
],
// codelyzer
"tslint.rulesDirectory": "./webapp/node_modules/codelyzer",
"typescript.tsdk": "webapp/node_modules/typescript/lib",
"tslint.configFile": "webapp/tslint.json"
-} \ No newline at end of file
+}
diff --git a/glide.yaml b/glide.yaml
index 3370e2d..1630276 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -25,3 +25,5 @@ import:
version: ^0.1.0
subpackages:
- golib/common
+- package: github.com/franciscocpg/reflectme
+ version: ^0.1.9
diff --git a/lib/agent/apiv1-exec.go b/lib/agent/apiv1-exec.go
index c199267..3cb4d23 100644
--- a/lib/agent/apiv1-exec.go
+++ b/lib/agent/apiv1-exec.go
@@ -5,15 +5,13 @@ import (
"io/ioutil"
"net/http"
+ "github.com/franciscocpg/reflectme"
"github.com/gin-gonic/gin"
"github.com/iotbzh/xds-agent/lib/apiv1"
common "github.com/iotbzh/xds-common/golib"
uuid "github.com/satori/go.uuid"
)
-var execCmdID = 1
-var fwdFuncID []uuid.UUID
-
// ExecCmd executes remotely a command
func (s *APIService) execCmd(c *gin.Context) {
s._execRequest("/exec", c)
@@ -81,6 +79,7 @@ func (s *APIService) _execRequest(cmd string, c *gin.Context) {
apiv1.ExecInferiorOutEvent,
}
+ var fwdFuncID []uuid.UUID
for _, evName := range evtList {
evN := evName
fwdFunc := func(pData interface{}, evData interface{}) error {
@@ -92,6 +91,9 @@ func (s *APIService) _execRequest(cmd string, c *gin.Context) {
return nil
}
+ // Add sessionID to event Data
+ reflectme.SetField(evData, "sessionID", sid)
+
// Forward event to Client/Dashboard
(*so).Emit(evN, evData)
return nil
@@ -110,15 +112,17 @@ func (s *APIService) _execRequest(cmd string, c *gin.Context) {
evN := apiv1.ExecExitEvent
sid := pData.(string)
+ // Add sessionID to event Data
+ reflectme.SetField(evData, "sessionID", sid)
+
// IO socket can be nil when disconnected
so := s.sessions.IOSocketGet(sid)
- if so == nil {
+ if so != nil {
+ (*so).Emit(evN, evData)
+ } else {
s.Log.Infof("%s not emitted: WS closed (sid:%s)", evN, sid)
- return nil
}
- (*so).Emit(evN, evData)
-
// cleanup listener
for i, evName := range evtList {
svr.EventOff(evName, fwdFuncID[i])
diff --git a/lib/agent/apiv1-projects.go b/lib/agent/apiv1-projects.go
index c835967..5784896 100644
--- a/lib/agent/apiv1-projects.go
+++ b/lib/agent/apiv1-projects.go
@@ -39,7 +39,7 @@ func (s *APIService) addProject(c *gin.Context) {
s.Log.Debugln("Add project config: ", cfgArg)
- newFld, err := s.projects.Add(cfgArg)
+ newFld, err := s.projects.Add(cfgArg, s.sessions.GetID(c))
if err != nil {
common.APIError(c, err.Error())
return
@@ -77,10 +77,34 @@ func (s *APIService) delProject(c *gin.Context) {
s.Log.Debugln("Delete project id ", id)
- delEntry, err := s.projects.Delete(id)
+ delEntry, err := s.projects.Delete(id, s.sessions.GetID(c))
if err != nil {
common.APIError(c, err.Error())
return
}
c.JSON(http.StatusOK, delEntry)
}
+
+// updateProject Update some field of a specific project
+func (s *APIService) updateProject(c *gin.Context) {
+ id, err := s.projects.ResolveID(c.Param("id"))
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
+
+ var cfgArg apiv1.ProjectConfig
+ if c.BindJSON(&cfgArg) != nil {
+ common.APIError(c, "Invalid arguments")
+ return
+ }
+
+ s.Log.Debugln("Update project id ", id)
+
+ upPrj, err := s.projects.Update(id, cfgArg, s.sessions.GetID(c))
+ if err != nil {
+ common.APIError(c, err.Error())
+ return
+ }
+ c.JSON(http.StatusOK, upPrj)
+}
diff --git a/lib/agent/apiv1.go b/lib/agent/apiv1.go
index 3e742f5..36e5a54 100644
--- a/lib/agent/apiv1.go
+++ b/lib/agent/apiv1.go
@@ -8,7 +8,7 @@ import (
"github.com/iotbzh/xds-agent/lib/xdsconfig"
)
-const apiBaseUrl = "/api/v1"
+const apiBaseURL = "/api/v1"
// APIService .
type APIService struct {
@@ -21,7 +21,7 @@ type APIService struct {
func NewAPIV1(ctx *Context) *APIService {
s := &APIService{
Context: ctx,
- apiRouter: ctx.webServer.router.Group(apiBaseUrl),
+ apiRouter: ctx.webServer.router.Group(apiBaseURL),
serverIndex: 0,
}
@@ -34,6 +34,7 @@ func NewAPIV1(ctx *Context) *APIService {
s.apiRouter.GET("/projects", s.getProjects)
s.apiRouter.GET("/projects/:id", s.getProject)
+ s.apiRouter.PUT("/projects/:id", s.updateProject)
s.apiRouter.POST("/projects", s.addProject)
s.apiRouter.POST("/projects/sync/:id", s.syncProject)
s.apiRouter.DELETE("/projects/:id", s.delProject)
@@ -80,7 +81,7 @@ func (s *APIService) AddXdsServer(cfg xdsconfig.XDSServerConf) (*XdsServer, erro
// Create a new server object
if cfg.APIBaseURL == "" {
- cfg.APIBaseURL = apiBaseUrl
+ cfg.APIBaseURL = apiBaseURL
}
if cfg.APIPartialURL == "" {
cfg.APIPartialURL = "/server/" + strconv.Itoa(s.serverIndex)
diff --git a/lib/agent/events.go b/lib/agent/events.go
index 9ff72ac..ccf8ddc 100644
--- a/lib/agent/events.go
+++ b/lib/agent/events.go
@@ -71,7 +71,7 @@ func (e *Events) UnRegister(evName, sessionID string) error {
}
// Emit Used to manually emit an event
-func (e *Events) Emit(evName string, data interface{}) error {
+func (e *Events) Emit(evName string, data interface{},fromSid string) error {
var firstErr error
if _, ok := e.eventsMap[evName]; !ok {
@@ -93,9 +93,10 @@ func (e *Events) Emit(evName string, data interface{}) error {
continue
}
msg := apiv1.EventMsg{
- Time: time.Now().String(),
- Type: evName,
- Data: data,
+ Time: time.Now().String(),
+ FromSessionID: fromSid,
+ Type: evName,
+ Data: data,
}
e.Log.Debugf("Emit Event %s: %v", evName, sid)
if err := (*so).Emit(evName, msg); err != nil {
diff --git a/lib/agent/project-interface.go b/lib/agent/project-interface.go
index c9e9ec5..0d6bb1a 100644
--- a/lib/agent/project-interface.go
+++ b/lib/agent/project-interface.go
@@ -4,11 +4,12 @@ import "github.com/iotbzh/xds-agent/lib/apiv1"
// IPROJECT Project interface
type IPROJECT interface {
- Add(cfg apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) // Add a new project
- Delete() error // Delete a project
- GetProject() *apiv1.ProjectConfig // Get project public configuration
- UpdateProject(prj apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) // Update project configuration
- GetServer() *XdsServer // Get XdsServer that holds this project
- Sync() error // Force project files synchronization
- IsInSync() (bool, error) // Check if project files are in-sync
+ Add(cfg apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) // Add a new project
+ Setup(prj apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) // Local setup of the project
+ Delete() error // Delete a project
+ GetProject() *apiv1.ProjectConfig // Get project public configuration
+ Update(prj apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) // Update project configuration
+ GetServer() *XdsServer // Get XdsServer that holds this project
+ Sync() error // Force project files synchronization
+ IsInSync() (bool, error) // Check if project files are in-sync
}
diff --git a/lib/agent/project-pathmap.go b/lib/agent/project-pathmap.go
index 7a96e6e..3c87770 100644
--- a/lib/agent/project-pathmap.go
+++ b/lib/agent/project-pathmap.go
@@ -69,7 +69,7 @@ func (p *PathMap) Add(cfg apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) {
// Send request to create folder on XDS server side
err = p.server.FolderAdd(fld, p.folder)
if err != nil {
- return nil, fmt.Errorf("Folders mapping verification failure.\n%v", err)
+ return nil, err
}
// 2nd part of sanity checker
@@ -98,16 +98,30 @@ func (p *PathMap) GetProject() *apiv1.ProjectConfig {
return &prj
}
-// UpdateProject Set project config
-func (p *PathMap) UpdateProject(prj apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) {
+// Setup Setup local project config
+func (p *PathMap) Setup(prj apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) {
p.folder = p.server.ProjectToFolder(prj)
np := p.GetProject()
- if err := p.events.Emit(apiv1.EVTProjectChange, np); err != nil {
+ if err := p.events.Emit(apiv1.EVTProjectChange, np, ""); err != nil {
return np, err
}
return np, nil
}
+// Update Update some field of a project
+func (p *PathMap) Update(prj apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) {
+ if p.folder.ID != prj.ID {
+ return nil, fmt.Errorf("Invalid id")
+ }
+
+ err := p.server.FolderUpdate(p.server.ProjectToFolder(prj), p.folder)
+ if err != nil {
+ return nil, err
+ }
+
+ return p.GetProject(), nil
+}
+
// GetServer Get the XdsServer that holds this project
func (p *PathMap) GetServer() *XdsServer {
return p.server
diff --git a/lib/agent/project-st.go b/lib/agent/project-st.go
index e2cd3cb..c4e8fce 100644
--- a/lib/agent/project-st.go
+++ b/lib/agent/project-st.go
@@ -1,6 +1,8 @@
package agent
import (
+ "fmt"
+
"github.com/iotbzh/xds-agent/lib/apiv1"
st "github.com/iotbzh/xds-agent/lib/syncthing"
)
@@ -56,8 +58,8 @@ func (p *STProject) Add(cfg apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) {
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)
+ // Use Setup function to setup remains fields
+ return p.Setup(*svrPrj)
}
// Delete a project
@@ -77,16 +79,16 @@ func (p *STProject) GetProject() *apiv1.ProjectConfig {
return &prj
}
-// UpdateProject Update project config
-func (p *STProject) UpdateProject(prj apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) {
+// Setup Setup local project config
+func (p *STProject) Setup(prj apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) {
// Update folder
p.folder = p.server.ProjectToFolder(prj)
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.server.EventOn("event:folder-state-change", "", p._cbServerFolderChanged)
+ if err := p.server.EventRegister("folder-state-change", svrPrj.ID); err != nil {
p.Log.Warningf("XDS Server EventRegister failed: %v", err)
return svrPrj, err
}
@@ -103,6 +105,21 @@ func (p *STProject) UpdateProject(prj apiv1.ProjectConfig) (*apiv1.ProjectConfig
return svrPrj, nil
}
+// Update Update some field of a project
+func (p *STProject) Update(prj apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) {
+
+ if p.folder.ID != prj.ID {
+ return nil, fmt.Errorf("Invalid id")
+ }
+
+ err := p.server.FolderUpdate(p.server.ProjectToFolder(prj), p.folder)
+ if err != nil {
+ return nil, err
+ }
+
+ return p.GetProject(), nil
+}
+
// GetServer Get the XdsServer that holds this project
func (p *STProject) GetServer() *XdsServer {
return p.server
@@ -142,7 +159,7 @@ func (p *STProject) _cbServerFolderChanged(pData interface{}, data interface{})
p.folder.DataCloudSync.STSvrIsInSync = evt.Folder.IsInSync
p.folder.DataCloudSync.STSvrStatus = evt.Folder.Status
- if err := p.events.Emit(apiv1.EVTProjectChange, p.server.FolderToProject(*p.folder)); err != nil {
+ if err := p.events.Emit(apiv1.EVTProjectChange, p.server.FolderToProject(*p.folder), ""); err != nil {
p.Log.Warningf("Cannot notify project change (from server): %v", err)
}
}
@@ -181,7 +198,7 @@ func (p *STProject) _cbLocalSTEvents(ev st.Event, data *st.EventsCBData) {
p.folder.DataCloudSync.STLocIsInSync = inSync
p.folder.DataCloudSync.STLocStatus = sts
- if err := p.events.Emit(apiv1.EVTProjectChange, p.server.FolderToProject(*p.folder)); err != nil {
+ if err := p.events.Emit(apiv1.EVTProjectChange, p.server.FolderToProject(*p.folder), ""); err != nil {
p.Log.Warningf("Cannot notify project change (local): %v", err)
}
}
diff --git a/lib/agent/projects.go b/lib/agent/projects.go
index f089882..966c231 100644
--- a/lib/agent/projects.go
+++ b/lib/agent/projects.go
@@ -6,6 +6,7 @@ import (
"strings"
"time"
+ "github.com/franciscocpg/reflectme"
"github.com/iotbzh/xds-agent/lib/apiv1"
"github.com/iotbzh/xds-agent/lib/syncthing"
"github.com/syncthing/syncthing/lib/sync"
@@ -119,14 +120,14 @@ func (p *Projects) GetProjectArrUnsafe() []apiv1.ProjectConfig {
}
// Add adds a new folder
-func (p *Projects) Add(newF apiv1.ProjectConfig) (*apiv1.ProjectConfig, error) {
+func (p *Projects) Add(newF apiv1.ProjectConfig, fromSid string) (*apiv1.ProjectConfig, error) {
prj, err := p.createUpdate(newF, true, false)
if err != nil {
return prj, err
}
// Notify client with event
- if err := p.events.Emit(apiv1.EVTProjectAdd, *prj); err != nil {
+ if err := p.events.Emit(apiv1.EVTProjectAdd, *prj, fromSid); err != nil {
p.Log.Warningf("Cannot notify project deletion: %v", err)
}
@@ -190,7 +191,7 @@ func (p *Projects) createUpdate(newF apiv1.ProjectConfig, create bool, initial b
}
} else {
// Just update project config
- if newPrj, err = fld.UpdateProject(newF); err != nil {
+ if newPrj, err = fld.Setup(newF); err != nil {
newF.Status = apiv1.StatusErrorConfig
log.Printf("ERROR Updating project: %v\n", err)
return newPrj, err
@@ -217,7 +218,7 @@ func (p *Projects) createUpdate(newF apiv1.ProjectConfig, create bool, initial b
}
// Delete deletes a specific folder
-func (p *Projects) Delete(id string) (apiv1.ProjectConfig, error) {
+func (p *Projects) Delete(id, fromSid string) (apiv1.ProjectConfig, error) {
var err error
pjMutex.Lock()
@@ -238,7 +239,7 @@ func (p *Projects) Delete(id string) (apiv1.ProjectConfig, error) {
delete(p.projects, id)
// Notify client with event
- if err := p.events.Emit(apiv1.EVTProjectDelete, *prj); err != nil {
+ if err := p.events.Emit(apiv1.EVTProjectDelete, *prj, fromSid); err != nil {
p.Log.Warningf("Cannot notify project deletion: %v", err)
}
@@ -262,3 +263,50 @@ func (p *Projects) IsProjectInSync(id string) (bool, error) {
}
return (*fc).IsInSync()
}
+
+// Update Update some field of a project
+func (p *Projects) Update(id string, prj apiv1.ProjectConfig, fromSid string) (*apiv1.ProjectConfig, error) {
+
+ pjMutex.Lock()
+ defer pjMutex.Unlock()
+
+ fc, exist := p.projects[id]
+ if !exist {
+ return nil, fmt.Errorf("Unknown id")
+ }
+
+ // Copy current in a new object to change nothing in case of an error rises
+ newFld := apiv1.ProjectConfig{}
+ reflectme.Copy((*fc).GetProject(), &newFld)
+
+ // Only update some fields
+ dirty := false
+ for _, fieldName := range apiv1.ProjectConfigUpdatableFields {
+ valNew, err := reflectme.GetField(prj, fieldName)
+ if err == nil {
+ valCur, err := reflectme.GetField(newFld, fieldName)
+ if err == nil && valNew != valCur {
+ err = reflectme.SetField(&newFld, fieldName, valNew)
+ if err != nil {
+ return nil, err
+ }
+ dirty = true
+ }
+ }
+ }
+
+ if !dirty {
+ return &newFld, nil
+ }
+
+ upPrj, err := (*fc).Update(newFld)
+ if err != nil {
+ return nil, err
+ }
+
+ // Notify client with event
+ if err := p.events.Emit(apiv1.EVTProjectChange, *upPrj, fromSid); err != nil {
+ p.Log.Warningf("Cannot notify project change: %v", err)
+ }
+ return upPrj, err
+}
diff --git a/lib/agent/sessions.go b/lib/agent/sessions.go
index 7347480..3d8b0f4 100644
--- a/lib/agent/sessions.go
+++ b/lib/agent/sessions.go
@@ -125,6 +125,14 @@ func (s *Sessions) Get(c *gin.Context) *ClientSession {
return nil
}
+// GetID returns the session or an empty string
+func (s *Sessions) GetID(c *gin.Context) string {
+ if sess := s.Get(c); sess != nil {
+ return sess.ID
+ }
+ return ""
+}
+
// IOSocketGet Get socketio definition from sid
func (s *Sessions) IOSocketGet(sid string) *socketio.Socket {
s.mutex.Lock()
diff --git a/lib/agent/xdsserver.go b/lib/agent/xdsserver.go
index 73a5bd9..7b03579 100644
--- a/lib/agent/xdsserver.go
+++ b/lib/agent/xdsserver.go
@@ -64,9 +64,12 @@ type XdsBuilderConfig struct {
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
@@ -78,6 +81,8 @@ type XdsFolderConfig 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
+
// Specific data depending on which Type is used
DataPathMap XdsPathMapConfig `json:"dataPathMap,omitempty"`
DataCloudSync XdsCloudSyncConfig `json:"dataCloudSync,omitempty"`
@@ -112,7 +117,7 @@ type XdsEventFolderChange struct {
Folder XdsFolderConfig `json:"folder"`
}
-// Event emitter callback
+// EventCB Event emitter callback
type EventCB func(privData interface{}, evtData interface{}) error
// caller Used to chain event listeners
@@ -241,6 +246,11 @@ func (xs *XdsServer) FolderSync(id string) error {
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 .
func (xs *XdsServer) SetAPIRouterGroup(r *gin.RouterGroup) {
xs.apiRouter = r
@@ -334,7 +344,7 @@ func (xs *XdsServer) EventOn(evName string, privData interface{}, f EventCB) (uu
// 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()
sEvts := make([]*caller, len(xs.sockEvents[evn]))
@@ -400,6 +410,7 @@ func (xs *XdsServer) ProjectToFolder(pPrj apiv1.ProjectConfig) *XdsFolderConfig
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,
@@ -408,6 +419,7 @@ func (xs *XdsServer) ProjectToFolder(pPrj apiv1.ProjectConfig) *XdsFolderConfig
Status: pPrj.Status,
IsInSync: pPrj.IsInSync,
DefaultSdk: pPrj.DefaultSdk,
+ ClientData: pPrj.ClientData,
DataPathMap: XdsPathMapConfig{
ServerPath: pPrj.ServerPath,
},
@@ -457,6 +469,7 @@ func (xs *XdsServer) FolderToProject(fPrj XdsFolderConfig) apiv1.ProjectConfig {
Status: sts,
IsInSync: inSync,
DefaultSdk: fPrj.DefaultSdk,
+ ClientData: fPrj.ClientData,
}
return pPrj
}
@@ -628,7 +641,7 @@ func (xs *XdsServer) _NotifyState() {
ConnRetry: xs.ConnRetry,
Connected: xs.Connected,
}
- if err := xs.events.Emit(apiv1.EVTServerConfig, evSts); err != nil {
+ if err := xs.events.Emit(apiv1.EVTServerConfig, evSts, ""); err != nil {
xs.Log.Warningf("Cannot notify XdsServer state change: %v", err)
}
}
diff --git a/lib/apiv1/events.go b/lib/apiv1/events.go
index cdd0889..b2fda62 100644
--- a/lib/apiv1/events.go
+++ b/lib/apiv1/events.go
@@ -40,9 +40,10 @@ var EVTAllList = []string{
// EventMsg Event message send over Websocket, data format depend to Type (see DecodeXXX function)
type EventMsg struct {
- Time string `json:"time"`
- Type string `json:"type"`
- Data interface{} `json:"data"`
+ Time string `json:"time"` // Timestamp
+ FromSessionID string `json:"sessionID"` // Session ID of client that emits this event
+ Type string `json:"type"` // Data type
+ Data interface{} `json:"data"` // Data
}
// DecodeServerCfg Helper to decode Data field type ServerCfg
diff --git a/lib/apiv1/projects.go b/lib/apiv1/projects.go
index d76fa09..b1e64c8 100644
--- a/lib/apiv1/projects.go
+++ b/lib/apiv1/projects.go
@@ -29,4 +29,10 @@ type ProjectConfig 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
+}
+
+// ProjectConfigUpdatableFields List fields that can be updated using Update function
+var ProjectConfigUpdatableFields = []string{
+ "Label", "DefaultSdk", "ClientData",
}
diff --git a/webapp/.stylelintrc.json b/webapp/.stylelintrc.json
index 9732970..8132883 100644
--- a/webapp/.stylelintrc.json
+++ b/webapp/.stylelintrc.json
@@ -63,7 +63,7 @@
"selector-pseudo-element-colon-notation": "double",
"selector-pseudo-element-no-unknown": true,
"selector-type-case": "lower",
- "selector-max-id": 0,
+ "selector-max-id": 1,
"no-missing-end-of-source-newline": true,
diff --git a/webapp/src/app/@core-xds/services/alert.service.spec.ts b/webapp/src/app/@core-xds/services/alert.service.spec.ts
index b3d364c..2de2ac3 100644
--- a/webapp/src/app/@core-xds/services/alert.service.spec.ts
+++ b/webapp/src/app/@core-xds/services/alert.service.spec.ts
@@ -3,13 +3,13 @@ import { TestBed, inject } from '@angular/core/testing';
import { AlertService } from './alert.service';
describe('AlertService', () => {
- beforeEach(() => {
- TestBed.configureTestingModule({
- providers: [AlertService]
- });
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [AlertService],
});
+ });
- it('should be created', inject([AlertService], (service: AlertService) => {
- expect(service).toBeTruthy();
- }));
+ it('should be created', inject([AlertService], (service: AlertService) => {
+ expect(service).toBeTruthy();
+ }));
});
diff --git a/webapp/src/app/@core-xds/services/alert.service.ts b/webapp/src/app/@core-xds/services/alert.service.ts
index c15e176..23a5e5d 100644
--- a/webapp/src/app/@core-xds/services/alert.service.ts
+++ b/webapp/src/app/@core-xds/services/alert.service.ts
@@ -31,7 +31,7 @@ export class AlertService {
public error(msg: string, dismissTime?: number) {
this.add({
- type: 'error', msg: msg, dismissible: true, dismissTimeout: dismissTime
+ type: 'error', msg: msg, dismissible: true, dismissTimeout: dismissTime,
});
}
diff --git a/webapp/src/app/@core-xds/services/build-settings.service.ts b/webapp/src/app/@core-xds/services/build-settings.service.ts
new file mode 100644
index 0000000..cb52ce3
--- /dev/null
+++ b/webapp/src/app/@core-xds/services/build-settings.service.ts
@@ -0,0 +1,78 @@
+import { Injectable } from '@angular/core';
+import { CookieService } from 'ngx-cookie';
+import { Observable } from 'rxjs/Observable';
+import { BehaviorSubject } from 'rxjs/BehaviorSubject';
+
+export interface IBuildSettings {
+ subpath: string;
+ cmdClean: string;
+ cmdPrebuild: string;
+ cmdBuild: string;
+ cmdPopulate: string;
+ cmdArgs: string[];
+ envVars: string[];
+}
+
+@Injectable()
+export class BuildSettingsService {
+ public settings$: Observable<IBuildSettings>;
+
+ private settingsSubject: BehaviorSubject<IBuildSettings>;
+ private settingsStore: IBuildSettings;
+
+ constructor(
+ private cookie: CookieService,
+ ) {
+ this._load();
+ }
+
+ // Load build settings from cookie
+ private _load() {
+ // Try to retrieve previous config from cookie
+ const cookConf = this.cookie.getObject('xds-build-settings');
+ if (cookConf != null) {
+ this.settingsStore = <IBuildSettings>cookConf;
+ } else {
+ // Set default config
+ this.settingsStore = {
+ subpath: '',
+ cmdClean: 'rm -rf build && echo Done',
+ cmdPrebuild: 'mkdir -p build && cd build && cmake ..',
+ cmdBuild: 'cd build && make',
+ cmdPopulate: 'cd build && make remote-target-populate',
+ cmdArgs: [],
+ envVars: [],
+ };
+ }
+ }
+
+ // Save config into cookie
+ private _save() {
+ // Notify subscribers
+ this.settingsSubject.next(Object.assign({}, this.settingsStore));
+
+ const cfg = Object.assign({}, this.settingsStore);
+ this.cookie.putObject('xds-build-settings', cfg);
+ }
+
+ // Get whole config values
+ get(): IBuildSettings {
+ return this.settingsStore;
+ }
+
+ // Get whole config values
+ set(bs: IBuildSettings) {
+ this.settingsStore = bs;
+ this._save();
+ }
+
+ get subpath(): string {
+ return this.settingsStore.subpath;
+ }
+
+ set subpath(p: string) {
+ this.settingsStore.subpath = p;
+ this._save();
+ }
+
+}
diff --git a/webapp/src/app/@core-xds/services/config.service.spec.ts b/webapp/src/app/@core-xds/services/config.service.spec.ts
index a20d4ba..f39b9d9 100644
--- a/webapp/src/app/@core-xds/services/config.service.spec.ts
+++ b/webapp/src/app/@core-xds/services/config.service.spec.ts
@@ -5,7 +5,7 @@ import { ConfigService } from './config.service';
describe('ConfigService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
- providers: [ConfigService]
+ providers: [ConfigService],
});
});
diff --git a/webapp/src/app/@core-xds/services/project.service.spec.ts b/webapp/src/app/@core-xds/services/project.service.spec.ts
index b8edfc7..0924a73 100644
--- a/webapp/src/app/@core-xds/services/project.service.spec.ts
+++ b/webapp/src/app/@core-xds/services/project.service.spec.ts
@@ -5,7 +5,7 @@ import { ProjectService } from './project.service';
describe('ProjectService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
- providers: [ProjectService]
+ providers: [ProjectService],
});
});
diff --git a/webapp/src/app/@core-xds/services/project.service.ts b/webapp/src/app/@core-xds/services/project.service.ts
index 8aeed80..94469fe 100644
--- a/webapp/src/app/@core-xds/services/project.service.ts
+++ b/webapp/src/app/@core-xds/services/project.service.ts
@@ -1,4 +1,4 @@
-import { Injectable, SecurityContext } from '@angular/core';
+import { Injectable, SecurityContext, isDevMode } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
@@ -15,12 +15,12 @@ export type ProjectTypeEnum = '' | 'PathMap' | 'CloudSync';
export const ProjectType = {
UNSET: '',
NATIVE_PATHMAP: 'PathMap',
- SYNCTHING: 'CloudSync'
+ SYNCTHING: 'CloudSync',
};
export const ProjectTypes = [
{ value: ProjectType.NATIVE_PATHMAP, display: 'Path mapping' },
- { value: ProjectType.SYNCTHING, display: 'Cloud Sync' }
+ { value: ProjectType.SYNCTHING, display: 'Cloud Sync' },
];
export const ProjectStatus = {
@@ -28,9 +28,18 @@ export const ProjectStatus = {
Disable: 'Disable',
Enable: 'Enable',
Pause: 'Pause',
- Syncing: 'Syncing'
+ Syncing: 'Syncing',
};
+export interface IUISettings {
+ subpath: string;
+ cmdClean: string;
+ cmdPrebuild: string;
+ cmdBuild: string;
+ cmdPopulate: string;
+ cmdArgs: string[];
+ envVars: string[];
+}
export interface IProject {
id?: string;
serverId: string;
@@ -45,95 +54,84 @@ export interface IProject {
isExpanded?: boolean;
visible?: boolean;
defaultSdkID?: string;
+ uiSettings?: IUISettings;
}
+const defaultUISettings: IUISettings = {
+ subpath: '',
+ cmdClean: 'rm -rf build && echo Done',
+ cmdPrebuild: 'mkdir -p build && cd build && cmake ..',
+ cmdBuild: 'cd build && make',
+ cmdPopulate: 'cd build && make remote-target-populate',
+ cmdArgs: [],
+ envVars: [],
+};
+
@Injectable()
export class ProjectService {
- public Projects$: Observable<IProject[]>;
+ projects$: Observable<IProject[]>;
+ curProject$: Observable<IProject>;
private _prjsList: IProject[] = [];
- private current: IProject;
private prjsSubject = <BehaviorSubject<IProject[]>>new BehaviorSubject(this._prjsList);
+ private _current: IProject;
+ private curPrjSubject = <BehaviorSubject<IProject>>new BehaviorSubject(this._current);
constructor(private xdsSvr: XDSAgentService) {
- this.current = null;
- this.Projects$ = this.prjsSubject.asObservable();
+ this._current = null;
+ this.projects$ = this.prjsSubject.asObservable();
+ this.curProject$ = this.curPrjSubject.asObservable();
+ // Load initial projects list
this.xdsSvr.getProjects().subscribe((projects) => {
this._prjsList = [];
projects.forEach(p => {
this._addProject(p, true);
});
- this.prjsSubject.next(Object.assign([], this._prjsList));
- });
- // Update Project data
- this.xdsSvr.ProjectState$.subscribe(prj => {
- const i = this._getProjectIdx(prj.id);
- if (i >= 0) {
- // XXX for now, only isInSync and status may change
- this._prjsList[i].isInSync = prj.isInSync;
- this._prjsList[i].status = prj.status;
- this._prjsList[i].isUsable = this._isUsableProject(prj);
- this.prjsSubject.next(Object.assign([], this._prjsList));
+ // TODO: get previous val from xds-config service / cookie
+ if (this._prjsList.length > 0) {
+ this._current = this._prjsList[0];
+ this.curPrjSubject.next(this._current);
}
- });
- // Add listener on create and delete project events
- this.xdsSvr.addEventListener('event:project-add', (ev) => {
- if (ev && ev.data && ev.data.id) {
- this._addProject(ev.data);
- } else {
- console.log('Warning: received events with unknown data: ev=', ev);
- }
- });
- this.xdsSvr.addEventListener('event:project-delete', (ev) => {
- if (ev && ev.data && ev.data.id) {
- const idx = this._prjsList.findIndex(item => item.id === ev.data.id);
- if (idx === -1) {
- console.log('Warning: received events on unknown project id: ev=', ev);
- return;
- }
- this._prjsList.splice(idx, 1);
- this.prjsSubject.next(Object.assign([], this._prjsList));
- } else {
- console.log('Warning: received events with unknown data: ev=', ev);
- }
+ this.prjsSubject.next(this._prjsList);
});
+ // Add listener on projects creation, deletion and change events
+ this.xdsSvr.onProjectAdd().subscribe(prj => this._addProject(prj));
+ this.xdsSvr.onProjectDelete().subscribe(prj => this._delProject(prj));
+ this.xdsSvr.onProjectChange().subscribe(prj => this._updateProject(prj));
}
- public setCurrent(s: IProject) {
- this.current = s;
+ setCurrent(p: IProject): IProject | undefined {
+ if (!p) {
+ this._current = null;
+ return undefined;
+ }
+ return this.setCurrentById(p.id);
}
- public getCurrent(): IProject {
- return this.current;
+ setCurrentById(id: string): IProject | undefined {
+ const p = this._prjsList.find(item => item.id === id);
+ if (p) {
+ this._current = p;
+ this.curPrjSubject.next(this._current);
+ }
+ return this._current;
}
- public getCurrentId(): string {
- if (this.current && this.current.id) {
- return this.current.id;
- }
- return '';
+ getCurrent(): IProject {
+ return this._current;
}
- Add(prj: IProject): Observable<IProject> {
- const xdsPrj: IXDSProjectConfig = {
- id: '',
- serverId: prj.serverId,
- label: prj.label || '',
- clientPath: prj.pathClient.trim(),
- serverPath: prj.pathServer,
- type: prj.type,
- defaultSdkID: prj.defaultSdkID,
- };
+ add(prj: IProject): Observable<IProject> {
// Send config to XDS server
- return this.xdsSvr.addProject(xdsPrj)
+ return this.xdsSvr.addProject(this._convToIXdsProject(prj))
.map(xp => this._convToIProject(xp));
}
- Delete(prj: IProject): Observable<IProject> {
+ delete(prj: IProject): Observable<IProject> {
const idx = this._getProjectIdx(prj.id);
const delPrj = prj;
if (idx === -1) {
@@ -143,7 +141,7 @@ export class ProjectService {
.map(res => delPrj);
}
- Sync(prj: IProject): Observable<string> {
+ sync(prj: IProject): Observable<string> {
const idx = this._getProjectIdx(prj.id);
if (idx === -1) {
throw new Error('Invalid project id (id=' + prj.id + ')');
@@ -151,6 +149,17 @@ export class ProjectService {
return this.xdsSvr.syncProject(prj.id);
}
+ setSettings(prj: IProject): Observable<IProject> {
+ return this.xdsSvr.updateProject(this._convToIXdsProject(prj))
+ .map(xp => this._convToIProject(xp));
+ }
+
+ getDefaultSettings(): IUISettings {
+ return defaultUISettings;
+ }
+
+ /*** Private functions ***/
+
private _isUsableProject(p) {
return p && p.isInSync &&
(p.status === ProjectStatus.Enable) &&
@@ -161,7 +170,27 @@ export class ProjectService {
return this._prjsList.findIndex((item) => item.id === id);
}
+
+ private _convToIXdsProject(prj: IProject): IXDSProjectConfig {
+ const xPrj: IXDSProjectConfig = {
+ id: prj.id || '',
+ serverId: prj.serverId,
+ label: prj.label || '',
+ clientPath: prj.pathClient.trim(),
+ serverPath: prj.pathServer,
+ type: prj.type,
+ defaultSdkID: prj.defaultSdkID,
+ clientData: JSON.stringify(prj.uiSettings || defaultUISettings),
+ };
+ return xPrj;
+ }
+
private _convToIProject(rPrj: IXDSProjectConfig): IProject {
+ let settings = defaultUISettings;
+ if (rPrj.clientData && rPrj.clientData !== '') {
+ settings = JSON.parse(rPrj.clientData);
+ }
+
// Convert XDSFolderConfig to IProject
const pp: IProject = {
id: rPrj.id,
@@ -175,14 +204,15 @@ export class ProjectService {
isUsable: this._isUsableProject(rPrj),
defaultSdkID: rPrj.defaultSdkID,
serverPrjDef: Object.assign({}, rPrj), // do a copy
+ uiSettings: settings,
};
return pp;
}
- private _addProject(rPrj: IXDSProjectConfig, noNext?: boolean): IProject {
+ private _addProject(prj: IXDSProjectConfig, noNext?: boolean): IProject {
// Convert XDSFolderConfig to IProject
- const pp = this._convToIProject(rPrj);
+ const pp = this._convToIProject(prj);
// add new project
this._prjsList.push(pp);
@@ -199,9 +229,38 @@ export class ProjectService {
});
if (!noNext) {
- this.prjsSubject.next(Object.assign([], this._prjsList));
+ this.prjsSubject.next(this._prjsList);
}
return pp;
}
+
+ private _delProject(prj: IXDSProjectConfig) {
+ const idx = this._prjsList.findIndex(item => item.id === prj.id);
+ if (idx === -1) {
+ if (isDevMode) {
+ /* tslint:disable:no-console */
+ console.log('Warning: Try to delete project unknown id: prj=', prj);
+ }
+ return;
+ }
+ const delId = this._prjsList[idx].id;
+ this._prjsList.splice(idx, 1);
+ if (this._prjsList[idx].id === this._current.id) {
+ this.setCurrent(this._prjsList[0]);
+ }
+ this.prjsSubject.next(this._prjsList);
+ }
+
+ private _updateProject(prj: IXDSProjectConfig) {
+ const i = this._getProjectIdx(prj.id);
+ if (i >= 0) {
+ // XXX for now, only isInSync and status may change
+ this._prjsList[i].isInSync = prj.isInSync;
+ this._prjsList[i].status = prj.status;
+ this._prjsList[i].isUsable = this._isUsableProject(prj);
+ this.prjsSubject.next(this._prjsList);
+ }
+ }
+
}
diff --git a/webapp/src/app/@core-xds/services/xds-config.service.ts b/webapp/src/app/@core-xds/services/xds-config.service.ts
index 7559673..2f751a7 100644
--- a/webapp/src/app/@core-xds/services/xds-config.service.ts
+++ b/webapp/src/app/@core-xds/services/xds-config.service.ts
@@ -47,7 +47,7 @@ export class XDSConfigService {
}
getCurServer(): IXDServerCfg {
- return Object.assign({}, this._curServer);
+ return this._curServer;
}
setCurServer(svr: IXDServerCfg): Observable<IXDServerCfg> {
@@ -58,7 +58,7 @@ export class XDSConfigService {
.map(cfg => this._updateCurServer())
.catch(err => {
this._curServer.connected = false;
- this.curServer$.next(Object.assign({}, this._curServer));
+ this.curServer$.next(this._curServer);
return Observable.throw(err);
});
} else {
@@ -66,7 +66,7 @@ export class XDSConfigService {
return this.xdsAgentSvr.setServerRetry(curSvr.id, svr.connRetry)
.map(cfg => this._updateCurServer())
.catch(err => {
- this.curServer$.next(Object.assign({}, this._curServer));
+ this.curServer$.next(this._curServer);
return Observable.throw(err);
});
}
@@ -76,7 +76,7 @@ export class XDSConfigService {
private _updateCurServer() {
this._curServer = this._getCurServer();
- this.curServer$.next(Object.assign({}, this._curServer));
+ this.curServer$.next(this._curServer);
}
private _getCurServer(url?: string): IXDServerCfg {
diff --git a/webapp/src/app/@core-xds/services/xdsagent.service.ts b/webapp/src/app/@core-xds/services/xdsagent.service.ts
index 56e493f..06ca557 100644
--- a/webapp/src/app/@core-xds/services/xdsagent.service.ts
+++ b/webapp/src/app/@core-xds/services/xdsagent.service.ts
@@ -1,4 +1,4 @@
-import { Injectable, Inject } from '@angular/core';
+import { Injectable, Inject, isDevMode } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { DOCUMENT } from '@angular/common';
import { Observable } from 'rxjs/Observable';
@@ -44,6 +44,7 @@ export interface IXDSProjectConfig {
status?: string;
isInSync?: boolean;
defaultSdkID: string;
+ clientData?: string;
}
export interface IXDSVer {
@@ -107,12 +108,16 @@ export class XDSAgentService {
public XdsConfig$: Observable<IXDSConfig>;
public Status$: Observable<IAgentStatus>;
- public ProjectState$ = <Subject<IXDSProjectConfig>>new Subject();
public CmdOutput$ = <Subject<ICmdOutput>>new Subject();
public CmdExit$ = <Subject<ICmdExit>>new Subject();
+ protected projectAdd$ = new Subject<IXDSProjectConfig>();
+ protected projectDel$ = new Subject<IXDSProjectConfig>();
+ protected projectChange$ = new Subject<IXDSProjectConfig>();
+
private baseUrl: string;
private wsUrl: string;
+ private httpSessionID: string;
private _config = <IXDSConfig>{ servers: [] };
private _status = { connected: false, servers: [] };
@@ -130,14 +135,25 @@ export class XDSAgentService {
const originUrl = this.document.location.origin;
this.baseUrl = originUrl + '/api/v1';
- const re = originUrl.match(/http[s]?:\/\/([^\/]*)[\/]?/);
- if (re === null || re.length < 2) {
- console.error('ERROR: cannot determine Websocket url');
- } else {
- this.wsUrl = 'ws://' + re[1];
- this._handleIoSocket();
- this._RegisterEvents();
- }
+ // Retrieve Session ID / token
+ this.http.get(this.baseUrl + '/version', { observe: 'response' })
+ .subscribe(
+ resp => {
+ this.httpSessionID = resp.headers.get('xds-agent-sid');
+
+ const re = originUrl.match(/http[s]?:\/\/([^\/]*)[\/]?/);
+ if (re === null || re.length < 2) {
+ console.error('ERROR: cannot determine Websocket url');
+ } else {
+ this.wsUrl = 'ws://' + re[1];
+ this._handleIoSocket();
+ this._RegisterEvents();
+ }
+ },
+ err => {
+ /* tslint:disable:no-console */
+ console.error('ERROR while retrieving session id:', err);
+ });
}
private _NotifyXdsAgentState(sts: boolean) {
@@ -182,6 +198,8 @@ export class XDSAgentService {
console.error('WS error:', err);
});
+ // XDS Events decoding
+
this.socket.on('make:output', data => {
this.CmdOutput$.next(Object.assign({}, <ICmdOutput>data));
});
@@ -198,8 +216,6 @@ export class XDSAgentService {
this.CmdExit$.next(Object.assign({}, <ICmdExit>data));
});
- // Events
- // (project-add and project-delete events are managed by project.service)
this.socket.on('event:server-config', ev => {
if (ev && ev.data) {
const cfg: IXDServerCfg = ev.data;
@@ -212,19 +228,52 @@ export class XDSAgentService {
}
});
+ this.socket.on('event:project-add', (ev) => {
+ if (ev && ev.data && ev.data.id) {
+ this.projectAdd$.next(Object.assign({}, ev.data));
+ if (ev.sessionID !== this.httpSessionID && ev.data.label) {
+ this.alert.info('Project "' + ev.data.label + '" has been added by another tool.');
+ }
+ } else if (isDevMode) {
+ /* tslint:disable:no-console */
+ console.log('Warning: received event:project-add with unknown data: ev=', ev);
+ }
+ });
+
+ this.socket.on('event:project-delete', (ev) => {
+ if (ev && ev.data && ev.data.id) {
+ this.projectDel$.next(Object.assign({}, ev.data));
+ if (ev.sessionID !== this.httpSessionID && ev.data.label) {
+ this.alert.info('Project "' + ev.data.label + '" has been deleted by another tool.');
+ }
+ } else if (isDevMode) {
+ console.log('Warning: received event:project-delete with unknown data: ev=', ev);
+ }
+ });
+
this.socket.on('event:project-state-change', ev => {
if (ev && ev.data) {
- this.ProjectState$.next(Object.assign({}, ev.data));
+ this.projectChange$.next(Object.assign({}, ev.data));
+ } else if (isDevMode) {
+ console.log('Warning: received event:project-state-change with unknown data: ev=', ev);
}
});
}
/**
- ** Events
+ ** Events registration
***/
- addEventListener(ev: string, fn: Function): SocketIOClient.Emitter {
- return this.socket.addEventListener(ev, fn);
+ onProjectAdd(): Observable<IXDSProjectConfig> {
+ return this.projectAdd$.asObservable();
+ }
+
+ onProjectDelete(): Observable<IXDSProjectConfig> {
+ return this.projectDel$.asObservable();
+ }
+
+ onProjectChange(): Observable<IXDSProjectConfig> {
+ return this.projectChange$.asObservable();
}
/**
@@ -307,6 +356,10 @@ export class XDSAgentService {
return this._delete('/projects/' + id);
}
+ updateProject(cfg: IXDSProjectConfig): Observable<IXDSProjectConfig> {
+ return this._put('/projects/' + cfg.id, cfg);
+ }
+
syncProject(id: string): Observable<string> {
return this._post('/projects/sync/' + id, {});
}
@@ -337,8 +390,8 @@ export class XDSAgentService {
res => { },
error => {
this.alert.error('ERROR while registering to all events: ' + error);
- }
- );
+ },
+ );
}
private _getServer(ID: string): IXDServerCfg {
@@ -371,6 +424,12 @@ export class XDSAgentService {
return this._decodeError(error);
});
}
+ private _put(url: string, body: any): Observable<any> {
+ return this.http.put(this.baseUrl + url, JSON.stringify(body), this._attachAuthHeaders())
+ .catch((error) => {
+ return this._decodeError(error);
+ });
+ }
private _delete(url: string): Observable<any> {
return this.http.delete(this.baseUrl + url, this._attachAuthHeaders())
.catch(this._decodeError);
@@ -391,7 +450,10 @@ export class XDSAgentService {
} else {
e = err.message ? err.message : err.toString();
}
- console.log('xdsagent.service - ERROR: ', e);
+ /* tslint:disable:no-console */
+ if (isDevMode) {
+ console.log('xdsagent.service - ERROR: ', e);
+ }
return Observable.throw(e);
}
}
diff --git a/webapp/src/app/@theme/components/header/header.component.html b/webapp/src/app/@theme/components/header/header.component.html
index 5d5eff6..4fa66b8 100644
--- a/webapp/src/app/@theme/components/header/header.component.html
+++ b/webapp/src/app/@theme/components/header/header.component.html
@@ -1,32 +1,37 @@
-<div class="header-container"
- [class.left]="position === 'normal'"
- [class.right]="position === 'inverse'">
+<div class="header-container" [class.left]="position === 'normal'" [class.right]="position === 'inverse'">
<div class="logo-containter">
- <a (click)="toggleSidebar()" href="#" class="navigation"><i class="nb-menu"></i></a>
-<!-- MODS_XDS
+ <a (click)="toggleSidebar()" href="#" class="navigation">
+ <i class="nb-menu"></i>
+ </a>
+ <!-- MODS_XDS
<div class="logo" (click)="goToHome()">ngx-<span>admin</span></div>
-->
- <div class="logo" (click)="goToHome()">XDS <span>dashboard</span></div>
+ <div class="logo" (click)="goToHome()">XDS
+ <span>dashboard</span>
+ </div>
</div>
-<!-- MODS_XDS
+ <!-- MODS_XDS
<ngx-theme-switcher></ngx-theme-switcher>
-->
</div>
-<nb-actions
- size="medium"
- class="header-container"
- [class.right]="position === 'normal'"
- [class.left]="position === 'inverse'">
+<nb-actions size="medium" class="header-container" [class.right]="position === 'normal'" [class.left]="position === 'inverse'">
+ <!-- MODS_XDS
<nb-action icon="nb-grid-b" class="toggle-layout" (click)="toggleSettings()"></nb-action>
+-->
<nb-action>
<nb-user [menu]="userMenu" [name]="user?.name" [picture]="user?.picture"></nb-user>
</nb-action>
- <nb-action class="control-item" disabled icon="nb-notifications"></nb-action>
-<!-- MODS_XDS
+ <nb-action class="control-item" disabled icon="nb-notifications">
+ </nb-action>
+ <nb-action icon="fa fa-question-circle-o">
+ </nb-action>
+
+ <!-- MODS_XDS
<nb-action class="control-item" icon="nb-email"></nb-action>
--->
<nb-action class="control-item">
<nb-search type="rotate-layout" (click)="startSearch()"></nb-search>
</nb-action>
+-->
+
</nb-actions>
diff --git a/webapp/src/app/pages/build/build-settings-modal/build-settings-modal.component.html b/webapp/src/app/pages/build/build-settings-modal/build-settings-modal.component.html
new file mode 100644
index 0000000..7dd2ec7
--- /dev/null
+++ b/webapp/src/app/pages/build/build-settings-modal/build-settings-modal.component.html
@@ -0,0 +1,60 @@
+<div class="modal-header">
+ <span>Build Settings</span>
+ <button class="close" aria-label="Close" (click)="closeModal()">
+ <span aria-hidden="true">&times;</span>
+ </button>
+</div>
+
+<div class="modal-body row">
+ <div class="col-12">
+ <form [formGroup]="settingsProjectForm" (ngSubmit)="onSubmit()">
+
+ <div class="form-group row">
+ <label for="clean-cmd" class="col-sm-3 col-form-label">Clean command</label>
+ <div class="col-sm-9">
+ <input type="text" id="inputCleanCmd" class="form-control" formControlName="cmdClean">
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <label for="prebuild-cmd" class="col-sm-3 col-form-label">Pre-Build command</label>
+ <div class="col-sm-9">
+ <input type="text" id="inputPrebuildCmd" class="form-control" formControlName="cmdPrebuild">
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <label for="build-cmd" class="col-sm-3 col-form-label">Build command</label>
+ <div class="col-sm-9">
+ <input type="text" id="inputBuildCmd" class="form-control" formControlName="cmdBuild">
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <label for="populate-cmd" class="col-sm-3 col-form-label">Populate command</label>
+ <div class="col-sm-9">
+ <input type="text" id="inputPopulateCmd" class="form-control" formControlName="cmdPopulate">
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <label for="envvars-cmd" class="col-sm-3 col-form-label">Env variables</label>
+ <div class="col-sm-9">
+ <input type="text" id="inputEnvVars" class="form-control" formControlName="envVars">
+ </div>
+ </div>
+
+ <div class="offset-sm-9 col-sm-9">
+ <button class="btn btn-sm btn-hero-secondary" (click)="closeAction=false; resetDefault()">Reset settings</button>
+ </div>
+ </form>
+ </div>
+</div>
+<div class="modal-footer form-group">
+ <div class="col-12">
+ <div class="offset-sm-4 col-sm-6">
+ <button class="btn btn-md btn-secondary" (click)="closeAction=false; closeModal()"> Cancel </button>
+ <button class="btn btn-md btn-primary" [disabled]="!settingsProjectForm.valid" (click)="closeAction=true; onSubmit()">Update</button>
+ </div>
+ </div>
+</div>
diff --git a/webapp/src/app/pages/build/build-settings-modal/build-settings-modal.component.ts b/webapp/src/app/pages/build/build-settings-modal/build-settings-modal.component.ts
new file mode 100644
index 0000000..01c6d1e
--- /dev/null
+++ b/webapp/src/app/pages/build/build-settings-modal/build-settings-modal.component.ts
@@ -0,0 +1,77 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { FormControl, FormGroup, Validators, ValidationErrors, FormBuilder, ValidatorFn, AbstractControl } from '@angular/forms';
+
+import { AlertService } from '../../../@core-xds/services/alert.service';
+import { ProjectService, IProject } from '../../../@core-xds/services/project.service';
+
+
+@Component({
+ selector: 'xds-build-settings-modal',
+ templateUrl: 'build-settings-modal.component.html',
+})
+
+export class BuildSettingsModalComponent implements OnInit {
+ // @Input('server-id') serverID: string;
+ private serverID: string;
+
+ closeAction = false;
+ userEditedLabel = false;
+
+ settingsProjectForm: FormGroup;
+ subpathCtrl = new FormControl('', Validators.nullValidator);
+
+ private curPrj: IProject;
+
+ constructor(
+ private alert: AlertService,
+ private projectSvr: ProjectService,
+ private fb: FormBuilder,
+ private activeModal: NgbActiveModal,
+ ) {
+ this.settingsProjectForm = fb.group({
+ subpath: this.subpathCtrl,
+ cmdClean: ['', Validators.required],
+ cmdPrebuild: ['', Validators.nullValidator],
+ cmdBuild: ['', Validators.required],
+ cmdPopulate: ['', Validators.nullValidator],
+ cmdArgs: ['', Validators.nullValidator],
+ envVars: ['', Validators.nullValidator],
+ });
+ }
+
+ ngOnInit() {
+ this.curPrj = this.projectSvr.getCurrent();
+ this.settingsProjectForm.patchValue(this.curPrj.uiSettings);
+ }
+
+ closeModal() {
+ this.activeModal.close();
+ }
+
+ resetDefault() {
+ this.settingsProjectForm.patchValue(this.projectSvr.getDefaultSettings());
+ }
+
+ onSubmit() {
+ if (!this.closeAction) {
+ return;
+ }
+
+ this.curPrj.uiSettings = this.settingsProjectForm.value;
+ this.projectSvr.setSettings(this.curPrj)
+ .subscribe(prj => {
+ this.alert.info('Settings of project "' + prj.label + '" successfully updated.');
+ this.closeModal();
+
+ // Reset Value for the next creation
+ this.settingsProjectForm.reset();
+ },
+ err => {
+ this.alert.error(err, 60);
+ this.closeModal();
+ });
+ }
+
+}
diff --git a/webapp/src/app/pages/build/build.component.html b/webapp/src/app/pages/build/build.component.html
index a1ef62d..1ce9484 100644
--- a/webapp/src/app/pages/build/build.component.html
+++ b/webapp/src/app/pages/build/build.component.html
@@ -11,43 +11,48 @@
</nb-actions>
</nb-card-body>
</div>
- <div class="col-md-12 col-lg-12 col-xxxl-6">
+ <div class="col-md-12 col-lg-12">
<nb-card size="xlarge">
<nb-tabset fullWidth>
- <nb-tab tabTitle="Build">
+ <nb-tab tabTitle="Build" style="overflow: hidden;">
- <div class="row" style="margin-top:1em;">
- <!-- FIXME SEB
- <button class="btn pull-right " (click)="reset() ">
- <span class="fa fa-eraser fa-size-x2"></span>
- </button>
- -->
- <div class="col-md-12 text-center ">
- <textarea rows="20" class="textarea-scroll" #scrollOutput>{{ cmdOutput }}</textarea>
- </div>
+ <div class="offset-md-10 col-md-2 right">
+ <i class="control-icon fa fa-eraser" (click)="resetOutput()">
+ </i>
+ </div>
+ <div class="col-md-12 text-center ">
+ <textarea rows="20" class="textarea-scroll" #scrollOutput>{{ cmdOutput }}</textarea>
</div>
<nb-card-body>
- <nb-actions size="medium" fullWidth>
- <nb-action (click)="clean()">
- <i class="fa fa-eraser"></i>
- <span>Clean</span>
- </nb-action>
- <nb-action (click)="preBuild()">
- <i class="nb-list"></i>
- <span>Pre-Build</span>
- </nb-action>
- <nb-action (click)="build()">
- <i class="fa fa-wrench"></i>
- <span>Build</span>
- </nb-action>
- <nb-action (click)="populate()">
- <i class="fa fa-send"></i>
- <span>Populate</span>
- </nb-action>
- </nb-actions>
- </nb-card-body>
+ <nb-actions size="medium" fullWidth>
+ <nb-action (click)="settingsShow()">
+ <i class="fa fa-cog"></i>
+ <span>Settings</span>
+ </nb-action>
+
+ <nb-action>
+ </nb-action>
+
+ <nb-action (click)="clean()">
+ <i class="fa fa-eraser"></i>
+ <span>Clean</span>
+ </nb-action>
+ <nb-action (click)="preBuild()">
+ <i class="nb-list"></i>
+ <span>Pre-Build</span>
+ </nb-action>
+ <nb-action (click)="build()">
+ <i class="fa fa-wrench"></i>
+ <span>Build</span>
+ </nb-action>
+ <nb-action (click)="populate()">
+ <i class="fa fa-send"></i>
+ <span>Populate</span>
+ </nb-action>
+ </nb-actions>
+ </nb-card-body>
</nb-tab>
diff --git a/webapp/src/app/pages/build/build.component.scss b/webapp/src/app/pages/build/build.component.scss
index b256f66..5308f3f 100644
--- a/webapp/src/app/pages/build/build.component.scss
+++ b/webapp/src/app/pages/build/build.component.scss
@@ -23,6 +23,12 @@
}
}
+.right {
+ display: flex;
+ flex-direction: row-reverse;
+ padding-right: 30px;
+}
+
nb-action {
i {
font-size: 2rem;
diff --git a/webapp/src/app/pages/build/build.component.spec.ts b/webapp/src/app/pages/build/build.component.spec.ts
index 016192c..a676a66 100644
--- a/webapp/src/app/pages/build/build.component.spec.ts
+++ b/webapp/src/app/pages/build/build.component.spec.ts
@@ -8,7 +8,7 @@ describe('BuildComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
- declarations: [ BuildComponent ]
+ declarations: [ BuildComponent ],
})
.compileComponents();
}));
diff --git a/webapp/src/app/pages/build/build.component.ts b/webapp/src/app/pages/build/build.component.ts
index 5adb9bc..99b7e54 100644
--- a/webapp/src/app/pages/build/build.component.ts
+++ b/webapp/src/app/pages/build/build.component.ts
@@ -1,11 +1,13 @@
import { Component, ViewEncapsulation, AfterViewChecked, ElementRef, ViewChild, OnInit, Input } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { FormControl, FormGroup, Validators, FormBuilder } from '@angular/forms';
-import { CookieService } from 'ngx-cookie';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/startWith';
+import { BuildSettingsModalComponent } from './build-settings-modal/build-settings-modal.component';
+
import { XDSAgentService, ICmdOutput } from '../../@core-xds/services/xdsagent.service';
import { ProjectService, IProject } from '../../@core-xds/services/project.service';
import { AlertService, IAlert } from '../../@core-xds/services/alert.service';
@@ -26,9 +28,6 @@ export class BuildComponent implements OnInit, AfterViewChecked {
// @Input() curProject: IProject;
@Input() curProject = <IProject>null;
- public buildForm: FormGroup;
- public subpathCtrl = new FormControl('', Validators.required);
- public debugEnable = false;
public buildIsCollapsed = false;
public cmdOutput: string;
public cmdInfo: string;
@@ -38,37 +37,16 @@ export class BuildComponent implements OnInit, AfterViewChecked {
constructor(
private prjSvr: ProjectService,
private xdsSvr: XDSAgentService,
- private fb: FormBuilder,
private alertSvr: AlertService,
private sdkSvr: SdkService,
- private cookie: CookieService,
+ private modalService: NgbModal,
) {
this.cmdOutput = '';
this.cmdInfo = ''; // TODO: to be remove (only for debug)
- this.buildForm = fb.group({
- subpath: this.subpathCtrl,
- cmdClean: ['', Validators.nullValidator],
- cmdPrebuild: ['', Validators.nullValidator],
- cmdBuild: ['', Validators.nullValidator],
- cmdPopulate: ['', Validators.nullValidator],
- cmdArgs: ['', Validators.nullValidator],
- envVars: ['', Validators.nullValidator],
- });
+
}
ngOnInit() {
- // Set default settings
- // TODO save & restore values from cookies
- this.buildForm.patchValue({
- subpath: '',
- cmdClean: 'rm -rf build',
- cmdPrebuild: 'mkdir -p build && cd build && cmake ..',
- cmdBuild: 'cd build && make',
- cmdPopulate: 'cd build && make remote-target-populate',
- cmdArgs: '',
- envVars: '',
- });
-
// Command output data tunneling
this.xdsSvr.CmdOutput$.subscribe(data => {
this.cmdOutput += data.stdout;
@@ -88,70 +66,77 @@ export class BuildComponent implements OnInit, AfterViewChecked {
});
this._scrollToBottom();
-
- // only use for debug
- this.debugEnable = (this.cookie.get('debug_build') === '1');
}
ngAfterViewChecked() {
this._scrollToBottom();
}
- reset() {
+ resetOutput() {
this.cmdOutput = '';
}
+ settingsShow() {
+ const activeModal = this.modalService.open(BuildSettingsModalComponent, { size: 'lg', container: 'nb-layout' });
+ activeModal.componentInstance.modalHeader = 'Large Modal';
+ }
+
clean() {
+ const curPrj = this.prjSvr.getCurrent();
this._exec(
- this.buildForm.value.cmdClean,
- this.buildForm.value.subpath,
+ curPrj.uiSettings.cmdClean,
+ curPrj.uiSettings.subpath,
[],
- this.buildForm.value.envVars);
+ curPrj.uiSettings.envVars.join(' '));
}
preBuild() {
+ const curPrj = this.prjSvr.getCurrent();
this._exec(
- this.buildForm.value.cmdPrebuild,
- this.buildForm.value.subpath,
+ curPrj.uiSettings.cmdPrebuild,
+ curPrj.uiSettings.subpath,
[],
- this.buildForm.value.envVars);
+ curPrj.uiSettings.envVars.join(' '));
}
build() {
+ const curPrj = this.prjSvr.getCurrent();
this._exec(
- this.buildForm.value.cmdBuild,
- this.buildForm.value.subpath,
+ curPrj.uiSettings.cmdBuild,
+ curPrj.uiSettings.subpath,
[],
- this.buildForm.value.envVars
+ curPrj.uiSettings.envVars.join(' '),
);
}
populate() {
+ const curPrj = this.prjSvr.getCurrent();
this._exec(
- this.buildForm.value.cmdPopulate,
- this.buildForm.value.subpath,
+ curPrj.uiSettings.cmdPopulate,
+ curPrj.uiSettings.subpath,
[], // args
- this.buildForm.value.envVars
+ curPrj.uiSettings.envVars.join(' '),
);
}
execCmd() {
+ const curPrj = this.prjSvr.getCurrent();
this._exec(
- this.buildForm.value.cmdArgs,
- this.buildForm.value.subpath,
+ curPrj.uiSettings.cmdArgs.join(' '),
+ curPrj.uiSettings.subpath,
[],
- this.buildForm.value.envVars
+ curPrj.uiSettings.envVars.join(' '),
);
}
private _exec(cmd: string, dir: string, args: string[], env: string) {
+ this.curProject = this.prjSvr.getCurrent();
+ const prjID = this.curProject.id;
+
if (!this.curProject) {
- this.alertSvr.warning('No active project', true);
+ return this.alertSvr.warning('No active project', true);
}
- // const prjID = this.curProject.id;
- const prjID = this.prjSvr.getCurrent().id;
-
this.cmdOutput += this._outputHeader();
const sdkid = this.sdkSvr.getCurrentId();
diff --git a/webapp/src/app/pages/build/build.module.ts b/webapp/src/app/pages/build/build.module.ts
index ac1dfab..34f05f2 100644
--- a/webapp/src/app/pages/build/build.module.ts
+++ b/webapp/src/app/pages/build/build.module.ts
@@ -2,6 +2,7 @@ import { NgModule } from '@angular/core';
import { ThemeModule } from '../../@theme/theme.module';
import { BuildComponent } from './build.component';
+import { BuildSettingsModalComponent } from './build-settings-modal/build-settings-modal.component';
import { ProjectSelectDropdownComponent } from './settings/project-select-dropdown.component';
import { SdkSelectDropdownComponent } from './settings/sdk-select-dropdown.component';
@@ -11,10 +12,12 @@ import { SdkSelectDropdownComponent } from './settings/sdk-select-dropdown.compo
],
declarations: [
BuildComponent,
+ BuildSettingsModalComponent,
ProjectSelectDropdownComponent,
SdkSelectDropdownComponent,
],
entryComponents: [
+ BuildSettingsModalComponent,
],
})
export class BuildModule { }
diff --git a/webapp/src/app/pages/build/settings-modal/build-settings-modal.component.ts b/webapp/src/app/pages/build/settings-modal/build-settings-modal.component.ts
new file mode 100644
index 0000000..fd1b904
--- /dev/null
+++ b/webapp/src/app/pages/build/settings-modal/build-settings-modal.component.ts
@@ -0,0 +1,143 @@
+import { Component, Input, ViewChild, OnInit } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { FormControl, FormGroup, Validators, ValidationErrors, FormBuilder, ValidatorFn, AbstractControl } from '@angular/forms';
+
+// Import RxJs required methods
+import 'rxjs/add/operator/map';
+import 'rxjs/add/operator/filter';
+import 'rxjs/add/operator/debounceTime';
+
+import { AlertService, IAlert } from '../../../@core-xds/services/alert.service';
+import { ProjectService, IProject, ProjectType, ProjectTypes } from '../../../@core-xds/services/project.service';
+import { XDSConfigService } from '../../../@core-xds/services/xds-config.service';
+
+
+@Component({
+ selector: 'xds-build-settings-modal',
+ templateUrl: 'build-settings-modal.component.html',
+ styleUrls: ['build-settings-modal.component.scss'],
+})
+export class BuildSettingsModalComponent implements OnInit {
+ // @Input('server-id') serverID: string;
+ private serverID: string;
+
+ cancelAction = false;
+ userEditedLabel = false;
+ projectTypes = ProjectTypes;
+
+ addProjectForm: FormGroup;
+ typeCtrl: FormControl;
+ pathCliCtrl: FormControl;
+ pathSvrCtrl: FormControl;
+
+ constructor(
+ private alert: AlertService,
+ private projectSvr: ProjectService,
+ private XdsConfigSvr: XDSConfigService,
+ private fb: FormBuilder,
+ private activeModal: NgbActiveModal,
+ ) {
+ // Define types (first one is special/placeholder)
+ this.projectTypes.unshift({ value: ProjectType.UNSET, display: '--Select a type--' });
+
+ this.typeCtrl = new FormControl(this.projectTypes[0].value, this.validatorProjType);
+ this.pathCliCtrl = new FormControl('', this.validatorProjPath);
+ this.pathSvrCtrl = new FormControl({ value: '', disabled: true }, this.validatorProjPath);
+
+ this.addProjectForm = fb.group({
+ type: this.typeCtrl,
+ pathCli: this.pathCliCtrl,
+ pathSvr: this.pathSvrCtrl,
+ label: ['', Validators.nullValidator],
+ });
+ }
+
+
+ ngOnInit() {
+ // Update server ID
+ this.serverID = this.XdsConfigSvr.getCurServer().id;
+ this.XdsConfigSvr.onCurServer().subscribe(svr => this.serverID = svr.id);
+
+ // Auto create label name
+ this.pathCliCtrl.valueChanges
+ .debounceTime(100)
+ .filter(n => n)
+ .map(n => {
+ const last = n.split('/');
+ let nm = n;
+ if (last.length > 0) {
+ nm = last.pop();
+ if (nm === '' && last.length > 0) {
+ nm = last.pop();
+ }
+ }
+ return 'Project_' + nm;
+ })
+ .subscribe(value => {
+ if (value && !this.userEditedLabel) {
+ this.addProjectForm.patchValue({ label: value });
+ }
+ });
+
+ // Handle disabling of Server path
+ this.typeCtrl.valueChanges
+ .debounceTime(500)
+ .subscribe(valType => {
+ const dis = (valType === String(ProjectType.SYNCTHING));
+ this.pathSvrCtrl.reset({ value: '', disabled: dis });
+ });
+ }
+
+ closeModal() {
+ this.activeModal.close();
+ }
+
+ onKeyLabel(event: any) {
+ this.userEditedLabel = (this.addProjectForm.value.label !== '');
+ }
+
+ onChangeLocalProject(e) {
+ }
+
+ onSubmit() {
+ if (this.cancelAction) {
+ return;
+ }
+
+ const formVal = this.addProjectForm.value;
+
+ const type = formVal['type'].value;
+ this.projectSvr.add({
+ serverId: this.serverID,
+ label: formVal['label'],
+ pathClient: formVal['pathCli'],
+ pathServer: formVal['pathSvr'],
+ type: formVal['type'],
+ // FIXME: allow to set defaultSdkID from New Project config panel
+ })
+ .subscribe(prj => {
+ this.alert.info('Project ' + prj.label + ' successfully created.');
+ this.closeModal();
+
+ // Reset Value for the next creation
+ this.addProjectForm.reset();
+ const selectedType = this.projectTypes[0].value;
+ this.addProjectForm.patchValue({ type: selectedType });
+
+ },
+ err => {
+ this.alert.error(err, 60);
+ this.closeModal();
+ });
+ }
+
+ private validatorProjType(g: FormGroup): ValidationErrors | null {
+ return (g.value !== ProjectType.UNSET) ? null : { validatorProjType: { valid: false } };
+ }
+
+ private validatorProjPath(g: FormGroup): ValidationErrors | null {
+ return (g.disabled || g.value !== '') ? null : { validatorProjPath: { valid: false } };
+ }
+
+}
diff --git a/webapp/src/app/pages/build/settings/project-select-dropdown.component.ts b/webapp/src/app/pages/build/settings/project-select-dropdown.component.ts
index da3580a..a83ec0a 100644
--- a/webapp/src/app/pages/build/settings/project-select-dropdown.component.ts
+++ b/webapp/src/app/pages/build/settings/project-select-dropdown.component.ts
@@ -1,4 +1,5 @@
import { Component, OnInit, Input } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
import { IProject, ProjectService } from '../../../@core-xds/services/project.service';
@@ -7,33 +8,27 @@ import { IProject, ProjectService } from '../../../@core-xds/services/project.se
template: `
<div class="form-group">
<label>Project</label>
- <select class="form-control">
- <option *ngFor="let prj of projects" (click)="select(prj)">{{prj.label}}</option>
+ <select class="form-control" [(ngModel)]="curPrj" (click)="select()">
+ <option *ngFor="let prj of projects$ | async" [ngValue]="prj">{{ prj.label }}</option>
</select>
</div>
`,
})
export class ProjectSelectDropdownComponent implements OnInit {
- projects: IProject[];
+ projects$: Observable<IProject[]>;
curPrj: IProject;
- constructor(private prjSvr: ProjectService) { }
+ constructor(private projectSvr: ProjectService) { }
ngOnInit() {
- this.curPrj = this.prjSvr.getCurrent();
- this.prjSvr.Projects$.subscribe((s) => {
- if (s) {
- this.projects = s;
- if (this.curPrj === null || s.indexOf(this.curPrj) === -1) {
- this.prjSvr.setCurrent(this.curPrj = s.length ? s[0] : null);
- }
- }
- });
+ this.curPrj = this.projectSvr.getCurrent();
+ this.projects$ = this.projectSvr.projects$;
+ this.projectSvr.curProject$.subscribe(p => this.curPrj = p);
}
- select(s) {
- this.prjSvr.setCurrent(this.curPrj = s);
+ select() {
+ this.projectSvr.setCurrentById(this.curPrj.id);
}
}
diff --git a/webapp/src/app/pages/config/config-xds/downloadXdsAgent.component.ts b/webapp/src/app/pages/config/config-xds/downloadXdsAgent.component.ts
index 3901331..556316c 100644
--- a/webapp/src/app/pages/config/config-xds/downloadXdsAgent.component.ts
+++ b/webapp/src/app/pages/config/config-xds/downloadXdsAgent.component.ts
@@ -2,6 +2,8 @@ import { Component } from '@angular/core';
@Component({
selector: 'xds-dwnl-agent',
+ template: ``,
+ /* FIXME SEB: to be reworked
template: `
<template #popTemplate>
<h3>Install xds-agent:</h3>
@@ -25,7 +27,8 @@ import { Component } from '@angular/core';
.fa-size-x2 {
font-size: 20px;
}
- `]
+ `],
+ */
})
export class DwnlAgentComponent {
diff --git a/webapp/src/app/pages/config/config.module.ts b/webapp/src/app/pages/config/config.module.ts
index 2fdaf94..d11bcf3 100644
--- a/webapp/src/app/pages/config/config.module.ts
+++ b/webapp/src/app/pages/config/config.module.ts
@@ -10,6 +10,6 @@ import { ConfigRoutingModule, routedConfig } from './config-routing.module';
],
declarations: [
...routedConfig,
- ]
+ ],
})
export class ConfigModule { }
diff --git a/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.html b/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.html
index e2c6748..9d26c71 100644
--- a/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.html
+++ b/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.html
@@ -39,13 +39,14 @@
<input type="text" id="inputLabel" class="form-control" formControlName="label" (keyup)="onKeyLabel($event)">
</div>
</div>
-
- <div class="offset-sm-3 col-sm-9">
- <button class="btn btn-md btn-secondary" (click)="cancelAction=true; closeModal()"> Cancel </button>
- <button class="btn btn-md btn-primary" type="submit" [disabled]="!addProjectForm.valid">Add Folder</button>
- </div>
</form>
</div>
</div>
<div class="modal-footer form-group">
+ <div class="col-12">
+ <div class="offset-sm-4 col-sm-6">
+ <button class="btn btn-md btn-secondary" (click)="cancelAction=true; closeModal()"> Cancel </button>
+ <button class="btn btn-md btn-primary" (click)="onSubmit()" [disabled]="!addProjectForm.valid">Add Folder</button>
+ </div>
+ </div>
</div>
diff --git a/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.ts b/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.ts
index 640ac5c..15b1b24 100644
--- a/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.ts
+++ b/webapp/src/app/pages/projects/project-add-modal/project-add-modal.component.ts
@@ -16,7 +16,7 @@ import { XDSConfigService } from '../../../@core-xds/services/xds-config.service
@Component({
selector: 'xds-project-add-modal',
templateUrl: 'project-add-modal.component.html',
- styleUrls: ['project-add-modal.component.scss']
+ styleUrls: ['project-add-modal.component.scss'],
})
export class ProjectAddModalComponent implements OnInit {
// @Input('server-id') serverID: string;
@@ -24,7 +24,7 @@ export class ProjectAddModalComponent implements OnInit {
cancelAction = false;
userEditedLabel = false;
- projectTypes = ProjectTypes;
+ projectTypes = Object.assign([], ProjectTypes);
addProjectForm: FormGroup;
typeCtrl: FormControl;
@@ -36,7 +36,7 @@ export class ProjectAddModalComponent implements OnInit {
private projectSvr: ProjectService,
private XdsConfigSvr: XDSConfigService,
private fb: FormBuilder,
- private activeModal: NgbActiveModal
+ private activeModal: NgbActiveModal,
) {
// Define types (first one is special/placeholder)
this.projectTypes.unshift({ value: ProjectType.UNSET, display: '--Select a type--' });
@@ -108,7 +108,7 @@ export class ProjectAddModalComponent implements OnInit {
const formVal = this.addProjectForm.value;
const type = formVal['type'].value;
- this.projectSvr.Add({
+ this.projectSvr.add({
serverId: this.serverID,
label: formVal['label'],
pathClient: formVal['pathCli'],
diff --git a/webapp/src/app/pages/projects/project-card/project-card.component.scss b/webapp/src/app/pages/projects/project-card/project-card.component.scss
index a433f58..6ac8d11 100644
--- a/webapp/src/app/pages/projects/project-card/project-card.component.scss
+++ b/webapp/src/app/pages/projects/project-card/project-card.component.scss
@@ -13,11 +13,6 @@ nb-card-footer {
text-align: right;
}
-.fa-big {
- font-size: 20px;
- font-weight: bold;
-}
-
.fa-size-x2 {
font-size: 20px;
}
@@ -31,14 +26,6 @@ th label {
margin-bottom: 0;
}
-tr.info>th {
- vertical-align: middle;
-}
-
-tr.info>td {
- vertical-align: middle;
-}
-
.btn-outline-danger.btn-xds {
color: #ff4c6a;
&:focus {
diff --git a/webapp/src/app/pages/projects/project-card/project-card.component.ts b/webapp/src/app/pages/projects/project-card/project-card.component.ts
index 160c4c8..840d656 100644
--- a/webapp/src/app/pages/projects/project-card/project-card.component.ts
+++ b/webapp/src/app/pages/projects/project-card/project-card.component.ts
@@ -17,28 +17,28 @@ export class ProjectCardComponent {
constructor(
private alert: AlertService,
- private projectSvr: ProjectService
+ private projectSvr: ProjectService,
) {
}
delete(prj: IProject) {
- this.projectSvr.Delete(prj).subscribe(
+ this.projectSvr.delete(prj).subscribe(
res => { },
- err => this.alert.error('ERROR delete: ' + err)
+ err => this.alert.error('ERROR delete: ' + err),
);
}
sync(prj: IProject) {
- this.projectSvr.Sync(prj).subscribe(
+ this.projectSvr.sync(prj).subscribe(
res => { },
- err => this.alert.error('ERROR: ' + err)
+ err => this.alert.error('ERROR: ' + err),
);
}
}
// Make Project type human readable
@Pipe({
- name: 'readableType'
+ name: 'readableType',
})
export class ProjectReadableTypePipe implements PipeTransform {
diff --git a/webapp/src/app/pages/projects/projects.component.html b/webapp/src/app/pages/projects/projects.component.html
index 662dfcc..ecce824 100644
--- a/webapp/src/app/pages/projects/projects.component.html
+++ b/webapp/src/app/pages/projects/projects.component.html
@@ -20,7 +20,7 @@
</div>
</nb-card-body>
</div>
- <div class="col-md-12 col-lg-12 col-xxxl-6" *ngFor="let prj of (projects$ | async)">
+ <div class="col-md-6 col-lg-6" *ngFor="let prj of (projects$ | async)">
<xds-project-card [project]="prj"></xds-project-card>
</div>
</div>
diff --git a/webapp/src/app/pages/projects/projects.component.scss b/webapp/src/app/pages/projects/projects.component.scss
index 3631fbb..92a8807 100644
--- a/webapp/src/app/pages/projects/projects.component.scss
+++ b/webapp/src/app/pages/projects/projects.component.scss
@@ -25,7 +25,7 @@
order: 1;
flex-direction: row-reverse;
}
- nb-actions>nb-action {
+ nb-actions > nb-action {
padding: 0;
}
nb-action {
diff --git a/webapp/src/app/pages/projects/projects.component.ts b/webapp/src/app/pages/projects/projects.component.ts
index 179bbd0..761ff50 100644
--- a/webapp/src/app/pages/projects/projects.component.ts
+++ b/webapp/src/app/pages/projects/projects.component.ts
@@ -23,7 +23,7 @@ export class ProjectsComponent implements OnInit {
}
ngOnInit() {
- this.projects$ = this.projectSvr.Projects$;
+ this.projects$ = this.projectSvr.projects$;
}
add() {
diff --git a/webapp/src/app/pages/projects/projects.module.ts b/webapp/src/app/pages/projects/projects.module.ts
index 48f37ce..960a2da 100644
--- a/webapp/src/app/pages/projects/projects.module.ts
+++ b/webapp/src/app/pages/projects/projects.module.ts
@@ -17,7 +17,7 @@ import { ProjectAddModalComponent } from './project-add-modal/project-add-modal.
ProjectReadableTypePipe,
],
entryComponents: [
- ProjectAddModalComponent
+ ProjectAddModalComponent,
],
})
export class ProjectsModule { }
diff --git a/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.scss b/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.scss
index e404610..a83cca4 100644
--- a/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.scss
+++ b/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.scss
@@ -13,11 +13,6 @@ nb-card-footer {
text-align: right;
}
-.fa-big {
- font-size: 20px;
- font-weight: bold;
-}
-
.fa-size-x2 {
font-size: 20px;
}
@@ -31,14 +26,6 @@ th label {
margin-bottom: 0;
}
-tr.info>th {
- vertical-align: middle;
-}
-
-tr.info>td {
- vertical-align: middle;
-}
-
.btn-outline-danger.btn-xds {
color: #ff4c6a;
&:focus {
diff --git a/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.ts b/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.ts
index 8228570..7135d36 100644
--- a/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.ts
+++ b/webapp/src/app/pages/sdks/sdk-card/sdk-card.component.ts
@@ -17,7 +17,7 @@ export class SdkCardComponent {
constructor(
private alert: AlertService,
- private sdkSvr: SdkService
+ private sdkSvr: SdkService,
) {
}
@@ -28,7 +28,7 @@ export class SdkCardComponent {
delete(sdk: ISdk) {
this.sdkSvr.delete(sdk).subscribe(
res => { },
- err => this.alert.error('ERROR delete: ' + err)
+ err => this.alert.error('ERROR delete: ' + err),
);
}
}
diff --git a/webapp/src/app/pages/sdks/sdks.component.html b/webapp/src/app/pages/sdks/sdks.component.html
index adfd924..94c2501 100644
--- a/webapp/src/app/pages/sdks/sdks.component.html
+++ b/webapp/src/app/pages/sdks/sdks.component.html
@@ -20,7 +20,7 @@
</div>
</nb-card-body>
</div>
- <div class="col-md-12 col-lg-12 col-xxxl-6" *ngFor="let sdk of (sdks$ | async)">
+ <div class="col-md-6 col-lg-6" *ngFor="let sdk of (sdks$ | async)">
<xds-sdk-card [sdk]="sdk"></xds-sdk-card>
</div>
</div>
diff --git a/webapp/src/app/pages/sdks/sdks.component.scss b/webapp/src/app/pages/sdks/sdks.component.scss
index 3631fbb..92a8807 100644
--- a/webapp/src/app/pages/sdks/sdks.component.scss
+++ b/webapp/src/app/pages/sdks/sdks.component.scss
@@ -25,7 +25,7 @@
order: 1;
flex-direction: row-reverse;
}
- nb-actions>nb-action {
+ nb-actions > nb-action {
padding: 0;
}
nb-action {
diff --git a/webapp/src/app/pages/sdks/sdks.component.ts b/webapp/src/app/pages/sdks/sdks.component.ts
index 3208121..ef933af 100644
--- a/webapp/src/app/pages/sdks/sdks.component.ts
+++ b/webapp/src/app/pages/sdks/sdks.component.ts
@@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
-//import { SdkAddModalComponent } from './sdk-add-modal/sdk-add-modal.component';
+// import { SdkAddModalComponent } from './sdk-add-modal/sdk-add-modal.component';
import { SdkService, ISdk } from '../../@core-xds/services/sdk.service';
diff --git a/webapp/src/app/pages/sdks/sdks.module.ts b/webapp/src/app/pages/sdks/sdks.module.ts
index 48629f6..c811eb4 100644
--- a/webapp/src/app/pages/sdks/sdks.module.ts
+++ b/webapp/src/app/pages/sdks/sdks.module.ts
@@ -3,7 +3,7 @@ import { ThemeModule } from '../../@theme/theme.module';
import { SdksComponent } from './sdks.component';
import { SdkCardComponent } from './sdk-card/sdk-card.component';
-//import { SdkAddModalComponent } from './sdk-add-modal/sdk-add-modal.component';
+// import { SdkAddModalComponent } from './sdk-add-modal/sdk-add-modal.component';
@NgModule({
@@ -13,10 +13,10 @@ import { SdkCardComponent } from './sdk-card/sdk-card.component';
declarations: [
SdksComponent,
SdkCardComponent,
- //SdkAddModalComponent,
+ // SdkAddModalComponent,
],
entryComponents: [
- //SdkAddModalComponent
+ // SdkAddModalComponent,
],
})
export class SdksModule { }