package models import ( "context" "errors" "cloud.o-forge.io/core/oc-catalog/models/rtype" "cloud.o-forge.io/core/oc-catalog/services" "github.com/beego/beego/v2/core/logs" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" ) // Assure consistency by using a const which refers to the MongoDB entry name // Workspace.Projects const WorkflowDB = "workflows" type Workspace struct { UserID string `bson:"_id" json:"user_id"` Workflows map[string]Workflow //WorkflowDB // ID: rtype Data []string `json:"data"` Computing []string `json:"computing"` Datacenter []string `json:"datacenter"` Storage []string `json:"storage"` } type ResourceModel interface { getRtype() rtype.Rtype getName() string } func (w Workspace) getRtype(rID string) (resModel rtype.Rtype) { for _, compVal := range w.Computing { if compVal == rID { return rtype.COMPUTING } } for _, datVal := range w.Data { if datVal == rID { return rtype.DATA } } for _, storVal := range w.Storage { if storVal == rID { return rtype.STORAGE } } for _, datcentVal := range w.Datacenter { if datcentVal == rID { return rtype.DATACENTER } } return rtype.INVALID } func (w *Workspace) GetResources() map[rtype.Rtype][]string { return map[rtype.Rtype][]string{ rtype.DATA: w.Data, rtype.COMPUTING: w.Computing, rtype.STORAGE: w.Storage, rtype.DATACENTER: w.Datacenter, } } func (w *Workspace) GetWorkflow(workflowName string) *Workflow { var proj Workflow proj = w.Workflows[workflowName] return &proj } func (w *Workspace) GetWorkflows() []string { if len(w.Workflows) == 0 { return nil } workflowNames := make([]string, len(w.Workflows)) i := 0 for k := range w.Workflows { workflowNames[i] = k i++ } return workflowNames } type WorkspaceModel struct { UserID string `bson:"_id" json:"user_id"` Data []DataModel `json:"data"` Computing []ComputingModel `json:"computing"` Datacenter []DatacenterModel `json:"datacenter"` Storage []StorageModel `json:"storage"` } func ListFullWorkspace(userID string) (*WorkspaceModel, error) { ws := GetWorkspace(userID) if ws == nil { return nil, errors.New("Internal error") } fws := &WorkspaceModel{ UserID: ws.UserID, Data: []DataModel{}, Computing: []ComputingModel{}, Datacenter: []DatacenterModel{}, Storage: []StorageModel{}, } pipeline := []primitive.M{ {"$match": primitive.M{"_id": userID}}, {"$lookup": primitive.M{ "localField": "data", "from": services.MngoNamesCollection.DATA, "foreignField": "_id", "as": "data", }}, {"$lookup": primitive.M{ "localField": "computing", "from": services.MngoNamesCollection.COMPUTING, "foreignField": "_id", "as": "computing", }}, {"$lookup": primitive.M{ "localField": "datacenter", "from": services.MngoNamesCollection.DATACENTER, "foreignField": "_id", "as": "datacenter", }}, {"$lookup": primitive.M{ "localField": "storage", "from": services.MngoNamesCollection.STORAGE, "foreignField": "_id", "as": "storage", }}, } ret, err := services.MngoCollWorkspace.Aggregate(services.MngoCtx, pipeline) if err != nil { message := "Couldn't obtain subobjects" logs.Debug(message + "; " + err.Error()) return nil, errors.New(message) } if ret.RemainingBatchLength() == 1 { ret.Next(context.Background()) ret.Decode(&fws) } return fws, nil } // Contains tells whether a contains x. func contains(a []string, x string) bool { for _, n := range a { if x == n { return true } } return false } func RemoveResource(userID, rID, rType string) error { rIDObj, err := primitive.ObjectIDFromHex(rID) if err != nil { message := "ID " + rID + " is not valid" logs.Debug(message + "; " + err.Error()) return errors.New(message) } result, err := services.MngoCollWorkspace.UpdateOne(services.MngoCtx, primitive.M{"_id": userID}, primitive.M{"$pull": primitive.M{rType: rIDObj}}, ) if err != nil { message := err.Error() logs.Debug(message) return errors.New(message) } if result.MatchedCount == 0 { message := "No user " + userID + " in workspace" logs.Debug(message) return errors.New(message) } if result.ModifiedCount == 0 { message := "No rID " + rID + " in rtype " + rType logs.Debug(message) return errors.New(message) } return nil } func (w *Workspace) updateDB() (err error) { _, err = services.MngoCollWorkspace.ReplaceOne(services.MngoCtx, primitive.M{"_id": w.UserID}, w, ) return } func (w *Workspace) NewResource(rID string, rType string) (err error) { var targetArray *[]string switch rType { case rtype.DATA.String(): targetArray = &w.Data case rtype.COMPUTING.String(): targetArray = &w.Computing case rtype.STORAGE.String(): targetArray = &w.Storage case rtype.DATACENTER.String(): targetArray = &w.Datacenter default: return errors.New("Rtype " + rType + " is not valid") } for _, models := range *targetArray { if models == rID { return errors.New("Resource " + rID + " of type " + rType + " is already registered for user " + w.UserID) } } *targetArray = append(*targetArray, rID) w.updateDB() return } func AddResource(userID, rID, rType string) (err error) { var rIDObj *primitive.ObjectID if rIDObj, err = IsValidResource(rID, rType); err != nil { return err } //TODO: Maybe operate directly in the DB instead retriving the full object? userWorkspace := GetWorkspace(userID) // Exist in the DB if userWorkspace != nil { var targetArray []string switch rType { case rtype.DATA.String(): targetArray = userWorkspace.Data case rtype.COMPUTING.String(): targetArray = userWorkspace.Computing case rtype.STORAGE.String(): targetArray = userWorkspace.Storage case rtype.DATACENTER.String(): targetArray = userWorkspace.Datacenter default: message := "Rtype " + rType + " is not valid" logs.Debug(message) return errors.New(message) } if ok := contains(targetArray, rID); ok { // Element already registered message := "Resource " + rID + " of type " + rType + " is already registered for user " + userID logs.Debug(message) return errors.New(message) } // New element // userWorkspace.ResourceList[rID] = rType _, err := services.MngoCollWorkspace.UpdateOne(services.MngoCtx, primitive.M{"_id": userID}, primitive.M{"$push": primitive.M{rType: rIDObj}}, ) if err != nil { message := "Internal error when updating in DB" logs.Debug(message + "; " + err.Error()) return errors.New(message) } return nil } return errors.New("Internal error") } func rTypeToCollection(rType string) (*mongo.Collection, error) { switch rType { case rtype.DATA.String(): return services.MngoCollData, nil case rtype.COMPUTING.String(): return services.MngoCollComputing, nil case rtype.DATACENTER.String(): return services.MngoCollDatacenter, nil case rtype.STORAGE.String(): return services.MngoCollStorage, nil } message := rType + " is not a valid resource type" logs.Debug(message) return nil, errors.New(message) } func IsValidResource(rID, rType string) (*primitive.ObjectID, error) { targetColl, err := rTypeToCollection(rType) if err != nil { return nil, err } rIDObj, err := primitive.ObjectIDFromHex(rID) if err != nil { message := "ID " + rID + " is not valid" logs.Debug(message + "; " + err.Error()) return nil, errors.New(message) } result := targetColl.FindOne(services.MngoCtx, primitive.M{"_id": rIDObj}) if result.Err() != nil { message := "ID " + rID + " doesn't exist for resource type " + rType logs.Debug(message + "; " + result.Err().Error()) return nil, errors.New(message) } return &rIDObj, nil } func GetAllWorkspaces() <-chan *Workspace { ch := make(chan *Workspace) go func() { cursor, err := services.MngoCollWorkspace.Find(services.MngoCtx, primitive.M{}) if err != nil { logs.Error(cursor.Err()) close(ch) } for cursor.Next(services.MngoCtx) { var item Workspace if err = cursor.Decode(&item); err != nil { logs.Error(err) close(ch) } ch <- &item } close(ch) // Remember to close or the loop never ends! }() return ch } func (w *Workspace) GetAllWorkspacesProjects() <-chan *Workflow { ch := make(chan *Workflow) go func() { for _, wproj := range w.Workflows { ch <- &wproj } close(ch) }() return ch } func GetWorkspace(userID string) (retObj *Workspace) { if err := services.MngoCollWorkspace.FindOne(services.MngoCtx, primitive.M{"_id": userID}).Decode(&retObj); err != nil { logs.Error(err.Error()) return nil } return } func NewWorkspace(userID string) (*Workspace, error) { newWsp := &Workspace{ UserID: userID, Data: []string{}, Computing: []string{}, Datacenter: []string{}, Storage: []string{}, } _, err := services.MngoCollWorkspace.InsertOne(services.MngoCtx, newWsp) if err != nil { logs.Warning(err.Error()) return nil, err } return newWsp, nil }