package oclib import ( "encoding/base64" "encoding/json" "errors" "fmt" "net/http" "strings" "runtime/debug" "cloud.o-forge.io/core/oc-lib/config" "cloud.o-forge.io/core/oc-lib/dbs" "cloud.o-forge.io/core/oc-lib/dbs/mongo" "cloud.o-forge.io/core/oc-lib/logs" "cloud.o-forge.io/core/oc-lib/models" "cloud.o-forge.io/core/oc-lib/models/collaborative_area" "cloud.o-forge.io/core/oc-lib/models/collaborative_area/rules/rule" "cloud.o-forge.io/core/oc-lib/models/order" "cloud.o-forge.io/core/oc-lib/models/peer" "cloud.o-forge.io/core/oc-lib/models/resources" "cloud.o-forge.io/core/oc-lib/models/utils" w2 "cloud.o-forge.io/core/oc-lib/models/workflow" "cloud.o-forge.io/core/oc-lib/models/workflow_execution" "cloud.o-forge.io/core/oc-lib/models/workspace" "cloud.o-forge.io/core/oc-lib/tools" beego "github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web/context" "github.com/goraz/onion" "github.com/rs/zerolog" ) type Filters = dbs.Filters type LibDataEnum int // init accessible constant to retrieve data from the database const ( INVALID LibDataEnum = iota DATA_RESOURCE = tools.DATA_RESOURCE PROCESSING_RESOURCE = tools.PROCESSING_RESOURCE STORAGE_RESOURCE = tools.STORAGE_RESOURCE COMPUTE_RESOURCE = tools.COMPUTE_RESOURCE WORKFLOW_RESOURCE = tools.WORKFLOW_RESOURCE WORKFLOW = tools.WORKFLOW WORKSPACE = tools.WORKSPACE WORKFLOW_EXECUTION = tools.WORKFLOW_EXECUTION PEER = tools.PEER COLLABORATIVE_AREA = tools.COLLABORATIVE_AREA RULE = tools.RULE BOOKING = tools.BOOKING ORDER = tools.ORDER ) // will turn into standards api hostnames func (d LibDataEnum) API() string { return tools.DefaultAPI[d] } // will turn into standards name func (d LibDataEnum) String() string { return tools.Str[d] } // will turn into enum index func (d LibDataEnum) EnumIndex() int { return int(d) } func IsQueryParamsEquals(input *context.BeegoInput, name string, val interface{}) bool { path := strings.Split(input.URI(), "?") if len(path) >= 2 { uri := strings.Split(path[1], "&") for _, val := range uri { kv := strings.Split(val, "=") if kv[0] == name && fmt.Sprintf("%v", val) == kv[1] { return true } } } return false } // model to define the shallow data structure type LibDataShallow struct { Data []utils.ShallowDBObject `bson:"data" json:"data"` Code int `bson:"code" json:"code"` Err string `bson:"error" json:"error"` } // model to define the data structure type LibData struct { Data utils.DBObject `bson:"data" json:"data"` Code int `bson:"code" json:"code"` Err string `bson:"error" json:"error"` } func InitDaemon(appName string) { config.SetAppName(appName) // set the app name to the logger to define the main log chan // create a temporary console logger for init logs.SetLogger(logs.CreateLogger("main")) // Load the right config file o := GetConfLoader() // feed the library with the loaded config SetConfig( o.GetStringDefault("MONGO_URL", "mongodb://127.0.0.1:27017"), o.GetStringDefault("MONGO_DATABASE", "DC_myDC"), o.GetStringDefault("NATS_URL", "nats://localhost:4222"), o.GetStringDefault("LOKI_URL", ""), o.GetStringDefault("LOG_LEVEL", "info"), ) // Beego init beego.BConfig.AppName = appName beego.BConfig.Listen.HTTPPort = o.GetIntDefault("port", 8080) beego.BConfig.WebConfig.DirectoryIndex = true beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger" } type IDTokenClaims struct { UserID string `json:"user_id"` PeerID string `json:"peer_id"` Groups []string `json:"groups"` } // SessionClaims struct type SessionClaims struct { AccessToken map[string]interface{} `json:"access_token"` IDToken IDTokenClaims `json:"id_token"` } // Claims struct type Claims struct { Session SessionClaims `json:"session"` } func ExtractTokenInfo(request http.Request) (string, string, []string) { reqToken := request.Header.Get("Authorization") splitToken := strings.Split(reqToken, "Bearer ") if len(splitToken) < 2 { reqToken = "" } else { reqToken = splitToken[1] } if reqToken != "" { token := strings.Split(reqToken, ".") if len(token) > 2 { bytes, err := base64.StdEncoding.DecodeString(token[2]) if err != nil { return "", "", []string{} } var c Claims err = json.Unmarshal(bytes, &c) if err != nil { return "", "", []string{} } return c.Session.IDToken.UserID, c.Session.IDToken.PeerID, c.Session.IDToken.Groups } } return "", "", []string{} } func Init(appName string) { InitDaemon(appName) api := &tools.API{} api.Discovered(beego.BeeApp.Handlers.GetAllControllerInfo()) } // // Expose subpackages // /* GetLogger returns the main logger * @return zerolog.Logger */ func GetLogger() zerolog.Logger { return logs.GetLogger() } /* SetConfig will set the config and create a logger according to app configuration and initialize mongo accessor * @param url string * @param database string * @param natsUrl string * @param lokiUrl string * @param logLevel string * @return *Config */ func SetConfig(mongoUrl string, database string, natsUrl string, lokiUrl string, logLevel string) *config.Config { cfg := config.SetConfig(mongoUrl, database, natsUrl, lokiUrl, logLevel) defer func() { if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in Init : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) fmt.Printf("Panic recovered in Init : %v - %v\n", r, string(debug.Stack())) } }() logs.CreateLogger("main") mongo.MONGOService.Init(models.GetModelsNames(), config.GetConfig()) // init the mongo service return cfg } /* GetConfig will get the config * @return *Config */ func GetConfig() *config.Config { return config.GetConfig() } /* GetConfLoader * Get the configuration loader for the application * Parameters: * - AppName: string : the name of the application * Returns: * - *onion.Onion : the configuration loader * The configuration loader will load the configuration from the following sources: * - the environment variables with the prefix OCAPPNAME_ * - the file /etc/oc/appname.json * - the file ./appname.json * The configuration loader will merge the configuration from the different sources * The configuration loader will give priority to the environment variables * The configuration loader will give priority to the local file over the default file */ func GetConfLoader() *onion.Onion { return config.GetConfLoader() } type Request struct { collection LibDataEnum user string peerID string groups []string caller *tools.HTTPCaller } func NewRequest(collection LibDataEnum, user string, peerID string, groups []string, caller *tools.HTTPCaller) *Request { return &Request{collection: collection, user: user, peerID: peerID, groups: groups, caller: caller} } func ToScheduler(m interface{}) (n *workflow_execution.WorkflowSchedule) { defer func() { if r := recover(); r != nil { return } }() return m.(*workflow_execution.WorkflowSchedule) } func (r *Request) Schedule(wfID string, scheduler *workflow_execution.WorkflowSchedule) (*workflow_execution.WorkflowSchedule, error) { if _, _, err := scheduler.Schedules(wfID, &tools.APIRequest{ Caller: r.caller, Username: r.user, PeerID: r.peerID, Groups: r.groups, }); err != nil { return nil, err } return scheduler, nil } func (r *Request) CheckBooking(wfID string, start string, end string, durationInS float64, cron string) bool { ok, _, _, err := workflow_execution.NewScheduler(start, end, durationInS, cron).CheckBooking(wfID, &tools.APIRequest{ Caller: r.caller, Username: r.user, PeerID: r.peerID, Groups: r.groups, }) if err != nil { fmt.Println(err) return false } return ok } func (r *Request) DraftOrder(scheduler *workflow_execution.WorkflowSchedule) (*order.Order, error) { o := &order.Order{} if err := o.DraftOrder(scheduler, &tools.APIRequest{ Caller: r.caller, Username: r.user, PeerID: r.peerID, Groups: r.groups, }); err != nil { return nil, err } return o, nil } func (r *Request) PaymentTunnel(o *order.Order, scheduler *workflow_execution.WorkflowSchedule) error { return o.Pay(scheduler, &tools.APIRequest{ Caller: r.caller, Username: r.user, PeerID: r.peerID, Groups: r.groups, }) } /* * Search will search for the data in the database * @param filters *dbs.Filters * @param word string * @param collection LibDataEnum * @param c ...*tools.HTTPCaller * @return data LibDataShallow */ func (r *Request) Search(filters *dbs.Filters, word string, isDraft bool) (data LibDataShallow) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in Search : "+fmt.Sprintf("%v", r))) data = LibDataShallow{Data: nil, Code: 500, Err: "Panic recovered in LoadAll : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() d, code, err := models.Model(r.collection.EnumIndex()).GetAccessor(&tools.APIRequest{ Caller: r.caller, Username: r.user, PeerID: r.peerID, Groups: r.groups, }).Search(filters, word, isDraft) if err != nil { data = LibDataShallow{Data: d, Code: code, Err: err.Error()} return } data = LibDataShallow{Data: d, Code: code} return } /* * LoadAll will load all the data from the database * @param collection LibDataEnum * @param c ...*tools.HTTPCaller * @return data LibDataShallow */ func (r *Request) LoadAll(isDraft bool) (data LibDataShallow) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in LoadAll : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) data = LibDataShallow{Data: nil, Code: 500, Err: "Panic recovered in LoadAll : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() d, code, err := models.Model(r.collection.EnumIndex()).GetAccessor(&tools.APIRequest{ Caller: r.caller, Username: r.user, PeerID: r.peerID, Groups: r.groups, }).LoadAll(isDraft) if err != nil { data = LibDataShallow{Data: d, Code: code, Err: err.Error()} return } data = LibDataShallow{Data: d, Code: code} return } /* * LoadOne will load one data from the database * @param collection LibDataEnum * @param id string * @param c ...*tools.HTTPCaller * @return data LibData */ func (r *Request) LoadOne(id string) (data LibData) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in LoadOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) data = LibData{Data: nil, Code: 500, Err: "Panic recovered in LoadOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() d, code, err := models.Model(r.collection.EnumIndex()).GetAccessor(&tools.APIRequest{ Caller: r.caller, Username: r.user, PeerID: r.peerID, Groups: r.groups, }).LoadOne(id) if err != nil { data = LibData{Data: d, Code: code, Err: err.Error()} return } data = LibData{Data: d, Code: code} return } /* * UpdateOne will update one data from the database * @param collection LibDataEnum * @param set map[string]interface{} * @param id string * @param c ...*tools.HTTPCaller * @return data LibData */ func (r *Request) UpdateOne(set map[string]interface{}, id string) (data LibData) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in UpdateOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) data = LibData{Data: nil, Code: 500, Err: "Panic recovered in UpdateOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() model := models.Model(r.collection.EnumIndex()) d, code, err := model.GetAccessor(&tools.APIRequest{ Caller: r.caller, Username: r.user, PeerID: r.peerID, Groups: r.groups, }).UpdateOne(model.Deserialize(set, model), id) if err != nil { data = LibData{Data: d, Code: code, Err: err.Error()} return } data = LibData{Data: d, Code: code} return } /* * DeleteOne will delete one data from the database * @param collection LibDataEnum * @param id string * @param c ...*tools.HTTPCaller * @return data LibData */ func (r *Request) DeleteOne(id string) (data LibData) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in DeleteOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) data = LibData{Data: nil, Code: 500, Err: "Panic recovered in DeleteOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() d, code, err := models.Model(r.collection.EnumIndex()).GetAccessor(&tools.APIRequest{ Caller: r.caller, Username: r.user, PeerID: r.peerID, Groups: r.groups, }).DeleteOne(id) if err != nil { data = LibData{Data: d, Code: code, Err: err.Error()} return } data = LibData{Data: d, Code: code} return } /* * StoreOne will store one data from the database * @param collection LibDataEnum * @param object map[string]interface{} * @param c ...*tools.HTTPCaller * @return data LibData */ func (r *Request) StoreOne(object map[string]interface{}) (data LibData) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in StoreOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) data = LibData{Data: nil, Code: 500, Err: "Panic recovered in StoreOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() model := models.Model(r.collection.EnumIndex()) d, code, err := model.GetAccessor(&tools.APIRequest{ Caller: r.caller, Username: r.user, PeerID: r.peerID, Groups: r.groups, }).StoreOne(model.Deserialize(object, model)) if err != nil { data = LibData{Data: d, Code: code, Err: err.Error()} return } data = LibData{Data: d, Code: code} return } /* * CopyOne will copy one data from the database * @param collection LibDataEnum * @param object map[string]interface{} * @param c ...*tools.HTTPCaller * @return data LibData */ func (r *Request) CopyOne(object map[string]interface{}) (data LibData) { defer func() { // recover the panic if r := recover(); r != nil { tools.UncatchedError = append(tools.UncatchedError, errors.New("Panic recovered in CopyOne : "+fmt.Sprintf("%v", r)+" - "+string(debug.Stack()))) data = LibData{Data: nil, Code: 500, Err: "Panic recovered in UpdateOne : " + fmt.Sprintf("%v", r) + " - " + string(debug.Stack())} } }() model := models.Model(r.collection.EnumIndex()) d, code, err := model.GetAccessor(&tools.APIRequest{ Caller: r.caller, Username: r.user, PeerID: r.peerID, Groups: r.groups, }).CopyOne(model.Deserialize(object, model)) if err != nil { data = LibData{Data: d, Code: code, Err: err.Error()} return } data = LibData{Data: d, Code: code} return } // ================ CAST ========================= // func (l *LibData) ToDataResource() *resources.DataResource { if l.Data.GetAccessor(nil).GetType() == tools.DATA_RESOURCE { return l.Data.(*resources.DataResource) } return nil } func (l *LibData) ToComputeResource() *resources.ComputeResource { if l.Data != nil && l.Data.GetAccessor(nil).GetType() == tools.COMPUTE_RESOURCE { return l.Data.(*resources.ComputeResource) } return nil } func (l *LibData) ToStorageResource() *resources.StorageResource { if l.Data.GetAccessor(nil).GetType() == tools.STORAGE_RESOURCE { return l.Data.(*resources.StorageResource) } return nil } func (l *LibData) ToProcessingResource() *resources.ProcessingResource { if l.Data.GetAccessor(nil).GetType() == tools.PROCESSING_RESOURCE { return l.Data.(*resources.ProcessingResource) } return nil } func (l *LibData) ToWorkflowResource() *resources.WorkflowResource { if l.Data.GetAccessor(nil).GetType() == tools.WORKFLOW_RESOURCE { return l.Data.(*resources.WorkflowResource) } return nil } func (l *LibData) ToPeer() *peer.Peer { if l.Data.GetAccessor(nil).GetType() == tools.PEER { return l.Data.(*peer.Peer) } return nil } func (l *LibData) ToWorkflow() *w2.Workflow { if l.Data.GetAccessor(nil).GetType() == tools.WORKFLOW { return l.Data.(*w2.Workflow) } return nil } func (l *LibData) ToWorkspace() *workspace.Workspace { if l.Data.GetAccessor(nil).GetType() == tools.WORKSPACE { return l.Data.(*workspace.Workspace) } return nil } func (l *LibData) ToCollaborativeArea() *collaborative_area.CollaborativeArea { if l.Data.GetAccessor(nil).GetType() == tools.COLLABORATIVE_AREA { return l.Data.(*collaborative_area.CollaborativeArea) } return nil } func (l *LibData) ToRule() *rule.Rule { if l.Data.GetAccessor(nil).GetType() == tools.COLLABORATIVE_AREA { return l.Data.(*rule.Rule) } return nil } func (l *LibData) ToWorkflowExecution() *workflow_execution.WorkflowExecutions { if l.Data.GetAccessor(nil).GetType() == tools.WORKFLOW_EXECUTION { return l.Data.(*workflow_execution.WorkflowExecutions) } return nil } func (l *LibData) ToOrder() *order.Order { if l.Data.GetAccessor(nil).GetType() == tools.ORDER { return l.Data.(*order.Order) } return nil }