package controllers import ( "encoding/json" "fmt" "strconv" oclib "cloud.o-forge.io/core/oc-lib" "cloud.o-forge.io/core/oc-lib/models/workspace" "cloud.o-forge.io/core/oc-lib/tools" "oc-workspace/infrastructure" beego "github.com/beego/beego/v2/server/web" ) // Operations about workspace type WorkspaceController struct { beego.Controller } var paths = map[tools.DataType]map[tools.METHOD]string{ tools.PEER: { // paths to call to status of peers tools.POST: "/status/", }, tools.WORKSPACE: { // paths to call to delete/update workspace on peer destination tools.PUT: "/:id?is_remote=true", tools.DELETE: "/:id?is_remote=true", }, } // @Title Search // @Description search workspace // @Param search path string true "the word search you want to get" // @Param is_draft query string false "draft wished" // @Param offset query string false "offset wished" // @Param limit query string false "limit wished" // @Success 200 {workspace} models.workspace // @router /search/:search [get] func (o *WorkspaceController) Search() { // user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request) // store and return Id or post with UUID search := o.Ctx.Input.Param(":search") isDraft := o.Ctx.Input.Query("is_draft") offset, _ := strconv.Atoi(o.Ctx.Input.Query("offset")) limit, _ := strconv.Atoi(o.Ctx.Input.Query("limit")) // o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.WORKSPACE), user, peerID, groups, nil).Search(nil, search, isDraft == "true") o.Data["json"] = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.WORKSPACE), nil).Search(nil, search, isDraft == "true", int64(offset), int64(limit)) o.ServeJSON() } // @Title Update // @Description create workspaces // @Param id path string true "the workspace id you want to get" // @Param body body models.workspace true "The workspace content" // @Success 200 {workspace} models.workspace // @router /:id [put] func (o *WorkspaceController) Put() { caller := tools.NewHTTPCaller(paths) caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true) var res map[string]interface{} id := o.Ctx.Input.Param(":id") json.Unmarshal(o.Ctx.Input.CopyBody(2000000), &res) // Snapshot resource IDs before the update to detect additions and removals. oldResources := loadWorkspaceResourceMap(id) o.Data["json"] = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.WORKSPACE), caller).UpdateOne(res, id) // Emit watch/unwatch events for resources that changed in this workspace. go diffAndEmitWatchers(oldResources, extractResourceMap(res)) o.ServeJSON() } // resourceEntry holds the resource ID and its creator's libp2p PeerID. type resourceEntry struct { creatorPeerID string dataType tools.DataType } // loadWorkspaceResourceMap returns the current resourceID→entry map for a workspace. func loadWorkspaceResourceMap(id string) map[string]resourceEntry { result := map[string]resourceEntry{} data := oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.WORKSPACE), nil).LoadOne(id) if data.Data == nil { return result } w, ok := data.Data.(*workspace.Workspace) if !ok { return result } for _, r := range w.DataResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.DATA_RESOURCE} } for _, r := range w.ComputeResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.COMPUTE_RESOURCE} } for _, r := range w.StorageResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.STORAGE_RESOURCE} } for _, r := range w.ProcessingResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.PROCESSING_RESOURCE} } for _, r := range w.WorkflowResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.WORKFLOW_RESOURCE} } for _, r := range w.ServiceResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.SERVICE_RESOURCE} } for _, r := range w.DynamicResources { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.DYNAMIC_RESOURCE} } for _, r := range w.NativeTools { result[r.GetID()] = resourceEntry{creatorPeerID: r.GetCreatorID(), dataType: tools.NATIVE_TOOL} } return result } // extractResourceMap parses the PUT body to build a resourceID→entry map. // creator_id is at the top level of each resource object (from Flutter toJSON()). var resourceArrayKeys = map[string]tools.DataType{ "data_resources": tools.DATA_RESOURCE, "compute_resources": tools.COMPUTE_RESOURCE, "storage_resources": tools.STORAGE_RESOURCE, "processing_resources": tools.PROCESSING_RESOURCE, "workflow_resources": tools.WORKFLOW_RESOURCE, "service_resources": tools.SERVICE_RESOURCE, "dynamic_resources": tools.DYNAMIC_RESOURCE, "native_tools": tools.NATIVE_TOOL, } func extractResourceMap(body map[string]interface{}) map[string]resourceEntry { result := map[string]resourceEntry{} for key, dt := range resourceArrayKeys { arr, ok := body[key].([]interface{}) if !ok { continue } for _, rawR := range arr { r, ok := rawR.(map[string]interface{}) if !ok { continue } id, _ := r["id"].(string) creatorID, _ := r["creator_id"].(string) if id != "" { result[id] = resourceEntry{creatorPeerID: creatorID, dataType: dt} } } } return result } // diffAndEmitWatchers emits PB_WATCH_RESOURCE for added non-self resources // and PB_UNWATCH_RESOURCE for removed ones. func diffAndEmitWatchers(old, new map[string]resourceEntry) { for id, entry := range new { if _, existed := old[id]; !existed { infrastructure.EmitWatchResource(id, entry.creatorPeerID, entry.dataType) } } for id, entry := range old { if _, stillPresent := new[id]; !stillPresent { infrastructure.EmitUnwatchResource(id, entry.creatorPeerID, entry.dataType) } } } // @Title Create // @Description create workspace // @Param data body json true "body for data content (Json format)" // @Success 200 {workspace} models.workspace // @router / [post] func (o *WorkspaceController) Post() { // user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request) caller := tools.NewHTTPCaller(paths) // generate a http caller to send to peer shared workspace caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true) var res map[string]interface{} json.Unmarshal(o.Ctx.Input.CopyBody(10000), &res) // o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.WORKSPACE), user, peerID, groups, caller).StoreOne(res) o.Data["json"] = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.WORKSPACE), caller).StoreOne(res) o.ServeJSON() } // @Title GetAll // @Description find workspace by id // @Param is_draft query string false "draft wished" // @Param offset query string false "offset wished" // @Param limit query string false "limit wished" // @Success 200 {workspace} models.workspace // @router / [get] func (o *WorkspaceController) GetAll() { user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request) fmt.Println(user, peerID, groups) isDraft := o.Ctx.Input.Query("is_draft") // o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.WORKSPACE), user, peerID, groups, nil).LoadAll(isDraft == "true") offset, _ := strconv.Atoi(o.Ctx.Input.Query("offset")) limit, _ := strconv.Atoi(o.Ctx.Input.Query("limit")) o.Data["json"] = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.WORKSPACE), nil).LoadAll(isDraft == "true", int64(offset), int64(limit)) o.ServeJSON() } // @Title Get // @Description find workflow by idisDraft := o.Ctx.Input.Query("is_draft") // @Param id path string true "the id you want to get" // @Success 200 {workspace} models.workspace // @router /:id [get] func (o *WorkspaceController) Get() { //user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request) id := o.Ctx.Input.Param(":id") // o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.WORKSPACE), user, peerID, groups, nil).LoadOne(id) o.Data["json"] = oclib.NewRequestAdmin(oclib.LibDataEnum(oclib.WORKSPACE), nil).LoadOne(id) o.ServeJSON() } // @Title Delete // @Description delete the workspace // @Param id path string true "The id you want to delete" // @Success 200 {workspace} delete success! // @router /:id [delete] func (o *WorkspaceController) Delete() { user, peerID, groups := oclib.ExtractTokenInfo(*o.Ctx.Request) id := o.Ctx.Input.Param(":id") caller := tools.NewHTTPCaller(paths) // generate a http caller to send to peer shared workspace caller.Disabled = oclib.IsQueryParamsEquals(o.Ctx.Input, "is_remote", true) o.Data["json"] = oclib.NewRequest(oclib.LibDataEnum(oclib.WORKSPACE), user, peerID, groups, caller).DeleteOne(id) o.ServeJSON() }