From 33b8d2799a3bf2971601a1db56ba4fff6ae4462c Mon Sep 17 00:00:00 2001 From: mr Date: Mon, 12 Jan 2026 11:59:05 +0100 Subject: [PATCH] update import plantUML --- models/order/order.go | 2 +- models/resources/compute.go | 38 +++++ models/resources/data.go | 36 +++++ models/resources/interfaces.go | 1 + models/resources/processing.go | 34 +++++ models/resources/resource.go | 15 ++ models/resources/storage.go | 38 +++++ models/resources/workflow.go | 3 + models/utils/abstracts.go | 4 + models/utils/interfaces.go | 1 + models/workflow/graph/graph.go | 9 ++ models/workflow/workflow.go | 257 +++++++++++++++++++++++++++++++++ 12 files changed, 437 insertions(+), 1 deletion(-) diff --git a/models/order/order.go b/models/order/order.go index a7819b0..ce566d1 100644 --- a/models/order/order.go +++ b/models/order/order.go @@ -44,7 +44,7 @@ func (o *Order) Quantity() int { return len(o.Purchases) + len(o.Purchases) } -func (d *Order) SetName() { +func (d *Order) SetName(_ string) { d.Name = d.UUID + "_order_" + "_" + time.Now().UTC().Format("2006-01-02T15:04:05") } diff --git a/models/resources/compute.go b/models/resources/compute.go index 7a184cc..64bc5db 100755 --- a/models/resources/compute.go +++ b/models/resources/compute.go @@ -10,6 +10,7 @@ import ( "cloud.o-forge.io/core/oc-lib/models/common/pricing" "cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/tools" + "github.com/google/uuid" ) /* @@ -60,6 +61,43 @@ type ComputeResourceInstance struct { Nodes []*ComputeNode `json:"nodes,omitempty" bson:"nodes,omitempty"` } +func NewComputeResourceInstance(name string, peerID string) ResourceInstanceITF { + return &ComputeResourceInstance{ + ResourceInstance: ResourceInstance[*ComputeResourcePartnership]{ + AbstractObject: utils.AbstractObject{ + UUID: uuid.New().String(), + Name: name, + }, + Partnerships: []*ComputeResourcePartnership{ + { + ResourcePartnerShip: ResourcePartnerShip[*ComputeResourcePricingProfile]{ + Namespace: "default", + PeerGroups: map[string][]string{ + peerID: {"*"}, + }, + PricingProfiles: map[int]map[int]*ComputeResourcePricingProfile{ + 0: { + 0: &ComputeResourcePricingProfile{ + ExploitPricingProfile: pricing.ExploitPricingProfile[pricing.TimePricingStrategy]{ + AccessPricingProfile: pricing.AccessPricingProfile[pricing.TimePricingStrategy]{ + Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{ + Price: 0, + Currency: "EUR", + TimePricingStrategy: pricing.ONCE, + BuyingStrategy: pricing.PERMANENT, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + type ComputeResourcePartnership struct { ResourcePartnerShip[*ComputeResourcePricingProfile] MinGaranteedCPUsCores map[string]float64 `json:"garanteed_cpus,omitempty" bson:"garanteed_cpus,omitempty"` diff --git a/models/resources/data.go b/models/resources/data.go index 9a26a20..a22afc2 100755 --- a/models/resources/data.go +++ b/models/resources/data.go @@ -9,6 +9,7 @@ import ( "cloud.o-forge.io/core/oc-lib/models/common/pricing" "cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/tools" + "github.com/google/uuid" ) /* @@ -53,6 +54,41 @@ type DataInstance struct { Source string `json:"source,omitempty" bson:"source,omitempty"` // Source is the source of the data } +func NewDataInstance(name string, peerID string) ResourceInstanceITF { + return &DataInstance{ + ResourceInstance: ResourceInstance[*DataResourcePartnership]{ + AbstractObject: utils.AbstractObject{ + UUID: uuid.New().String(), + Name: name, + }, + Partnerships: []*DataResourcePartnership{ + { + ResourcePartnerShip: ResourcePartnerShip[*DataResourcePricingProfile]{ + Namespace: "default", + PeerGroups: map[string][]string{ + peerID: {"*"}, + }, + PricingProfiles: map[int]map[int]*DataResourcePricingProfile{ + 0: { + 0: &DataResourcePricingProfile{ + AccessPricingProfile: pricing.AccessPricingProfile[DataResourcePricingStrategy]{ + Pricing: pricing.PricingStrategy[DataResourcePricingStrategy]{ + Price: 0, + Currency: "EUR", + TimePricingStrategy: pricing.ONCE, + BuyingStrategy: pricing.PERMANENT, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + func (ri *DataInstance) StoreDraftDefault() { found := false for _, p := range ri.ResourceInstance.Env { diff --git a/models/resources/interfaces.go b/models/resources/interfaces.go index 57622fd..1209c69 100755 --- a/models/resources/interfaces.go +++ b/models/resources/interfaces.go @@ -14,6 +14,7 @@ type ResourceInterface interface { GetSelectedInstance() ResourceInstanceITF ClearEnv() utils.DBObject SetAllowedInstances(request *tools.APIRequest) + AddInstances(instance ResourceInstanceITF) } type ResourceInstanceITF interface { diff --git a/models/resources/processing.go b/models/resources/processing.go index e6b6ea5..b3371ef 100755 --- a/models/resources/processing.go +++ b/models/resources/processing.go @@ -8,6 +8,7 @@ import ( "cloud.o-forge.io/core/oc-lib/models/common/pricing" "cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/tools" + "github.com/google/uuid" ) type ProcessingUsage struct { @@ -47,6 +48,39 @@ type ProcessingInstance struct { Access *ProcessingResourceAccess `json:"access,omitempty" bson:"access,omitempty"` // Access is the access } +func NewProcessingInstance(name string, peerID string) ResourceInstanceITF { + return &ProcessingInstance{ + ResourceInstance: ResourceInstance[*ResourcePartnerShip[*ProcessingResourcePricingProfile]]{ + AbstractObject: utils.AbstractObject{ + UUID: uuid.New().String(), + Name: name, + }, + Partnerships: []*ResourcePartnerShip[*ProcessingResourcePricingProfile]{ + { + Namespace: "default", + PeerGroups: map[string][]string{ + peerID: {"*"}, + }, + PricingProfiles: map[int]map[int]*ProcessingResourcePricingProfile{ + 0: { + 0: &ProcessingResourcePricingProfile{ + AccessPricingProfile: pricing.AccessPricingProfile[pricing.TimePricingStrategy]{ + Pricing: pricing.PricingStrategy[pricing.TimePricingStrategy]{ + Price: 0, + Currency: "EUR", + TimePricingStrategy: pricing.ONCE, + BuyingStrategy: pricing.PERMANENT, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + type PricedProcessingResource struct { PricedResource IsService bool diff --git a/models/resources/resource.go b/models/resources/resource.go index aa956c8..0f44a67 100755 --- a/models/resources/resource.go +++ b/models/resources/resource.go @@ -10,6 +10,7 @@ import ( "cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/tools" "github.com/biter777/countries" + "github.com/google/uuid" ) // AbstractResource is the struct containing all of the attributes commons to all ressources @@ -52,6 +53,10 @@ type AbstractInstanciatedResource[T ResourceInstanceITF] struct { Instances []T `json:"instances,omitempty" bson:"instances,omitempty"` // Bill is the bill of the resource // Bill is the bill of the resource } +func (abs *AbstractInstanciatedResource[T]) AddInstances(instance ResourceInstanceITF) { + abs.Instances = append(abs.Instances, instance.(T)) +} + func (abs *AbstractInstanciatedResource[T]) ConvertToPricedResource(t tools.DataType, request *tools.APIRequest) pricing.PricedItemITF { instances := map[string]string{} profiles := []pricing.PricingProfileITF{} @@ -157,6 +162,16 @@ type ResourceInstance[T ResourcePartnerITF] struct { Partnerships []T `json:"partnerships,omitempty" bson:"partnerships,omitempty"` } +func NewInstance[T ResourcePartnerITF](name string) *ResourceInstance[T] { + return &ResourceInstance[T]{ + AbstractObject: utils.AbstractObject{ + UUID: uuid.New().String(), + Name: name, + }, + Partnerships: []T{}, + } +} + func (ri *ResourceInstance[T]) ClearEnv() { ri.Env = []models.Param{} ri.Inputs = []models.Param{} diff --git a/models/resources/storage.go b/models/resources/storage.go index 77a04c1..5993ee1 100755 --- a/models/resources/storage.go +++ b/models/resources/storage.go @@ -10,6 +10,7 @@ import ( "cloud.o-forge.io/core/oc-lib/models/common/pricing" "cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/tools" + "github.com/google/uuid" ) /* @@ -54,6 +55,43 @@ type StorageResourceInstance struct { Throughput string `bson:"throughput,omitempty" json:"throughput,omitempty"` // Throughput is the throughput of the storage } +func NewStorageResourceInstance(name string, peerID string) ResourceInstanceITF { + return &StorageResourceInstance{ + ResourceInstance: ResourceInstance[*StorageResourcePartnership]{ + AbstractObject: utils.AbstractObject{ + UUID: uuid.New().String(), + Name: name, + }, + Partnerships: []*StorageResourcePartnership{ + { + ResourcePartnerShip: ResourcePartnerShip[*StorageResourcePricingProfile]{ + Namespace: "default", + PeerGroups: map[string][]string{ + peerID: {"*"}, + }, + PricingProfiles: map[int]map[int]*StorageResourcePricingProfile{ + 0: { + 0: &StorageResourcePricingProfile{ + ExploitPricingProfile: pricing.ExploitPricingProfile[StorageResourcePricingStrategy]{ + AccessPricingProfile: pricing.AccessPricingProfile[StorageResourcePricingStrategy]{ + Pricing: pricing.PricingStrategy[StorageResourcePricingStrategy]{ + Price: 0, + Currency: "EUR", + TimePricingStrategy: pricing.ONCE, + BuyingStrategy: pricing.PERMANENT, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + func (ri *StorageResourceInstance) ClearEnv() { ri.Env = []models.Param{} ri.Inputs = []models.Param{} diff --git a/models/resources/workflow.go b/models/resources/workflow.go index edba6f0..904b9fa 100755 --- a/models/resources/workflow.go +++ b/models/resources/workflow.go @@ -19,6 +19,9 @@ func (d *WorkflowResource) GetAccessor(request *tools.APIRequest) utils.Accessor return NewAccessor[*WorkflowResource](tools.WORKFLOW_RESOURCE, request, func() utils.DBObject { return &WorkflowResource{} }) } +func (r *WorkflowResource) AddInstances(instance ResourceInstanceITF) { +} + func (r *WorkflowResource) GetType() string { return tools.WORKFLOW_RESOURCE.String() } diff --git a/models/utils/abstracts.go b/models/utils/abstracts.go index 5c451ca..11f0ce2 100755 --- a/models/utils/abstracts.go +++ b/models/utils/abstracts.go @@ -47,6 +47,10 @@ func (r *AbstractObject) SetID(id string) { r.UUID = id } +func (r *AbstractObject) SetName(name string) { + r.Name = name +} + func (r *AbstractObject) GenerateID() { if r.UUID == "" { r.UUID = uuid.New().String() diff --git a/models/utils/interfaces.go b/models/utils/interfaces.go index 70e0ecc..11d1f39 100755 --- a/models/utils/interfaces.go +++ b/models/utils/interfaces.go @@ -21,6 +21,7 @@ type DBObject interface { SetID(id string) GetID() string GetName() string + SetName(name string) IsDrafted() bool CanDelete() bool StoreDraftDefault() diff --git a/models/workflow/graph/graph.go b/models/workflow/graph/graph.go index 991d321..577036a 100644 --- a/models/workflow/graph/graph.go +++ b/models/workflow/graph/graph.go @@ -15,6 +15,15 @@ type Graph struct { Links []GraphLink `bson:"links" json:"links" default:"{}" validate:"required"` // Links is the list of links between elements in the graph } +func NewGraph() *Graph { + return &Graph{ + Partial: false, + Zoom: 1, + Items: map[string]GraphItem{}, + Links: []GraphLink{}, + } +} + func (g *Graph) Clear(id string) { realItems := map[string]GraphItem{} for k, it := range g.Items { diff --git a/models/workflow/workflow.go b/models/workflow/workflow.go index 71f0a1f..44dca7d 100644 --- a/models/workflow/workflow.go +++ b/models/workflow/workflow.go @@ -1,7 +1,12 @@ package workflow import ( + "bufio" + "encoding/json" "errors" + "fmt" + "os" + "strings" "time" "cloud.o-forge.io/core/oc-lib/models/collaborative_area/shallow_collaborative_area" @@ -12,6 +17,7 @@ import ( "cloud.o-forge.io/core/oc-lib/models/utils" "cloud.o-forge.io/core/oc-lib/models/workflow/graph" "cloud.o-forge.io/core/oc-lib/tools" + "github.com/google/uuid" ) /* @@ -31,6 +37,257 @@ func (d *Workflow) GetAccessor(request *tools.APIRequest) utils.Accessor { return NewAccessor(request) // Create a new instance of the accessor } +func (d *Workflow) GetResources(dt tools.DataType) []resources.ResourceInterface { + itf := []resources.ResourceInterface{} + switch dt { + case tools.DATA_RESOURCE: + for _, d := range d.DataResources { + itf = append(itf, d) + } + return itf + case tools.PROCESSING_RESOURCE: + for _, d := range d.ProcessingResources { + itf = append(itf, d) + } + return itf + case tools.COMPUTE_RESOURCE: + for _, d := range d.ComputeResources { + itf = append(itf, d) + } + return itf + case tools.WORKFLOW_RESOURCE: + for _, d := range d.WorkflowResources { + itf = append(itf, d) + } + return itf + case tools.STORAGE_RESOURCE: + for _, d := range d.StorageResources { + itf = append(itf, d) + } + return itf + } + return itf +} + +func (d *Workflow) ExtractFromPlantUML(plantUML *os.File, request *tools.APIRequest) (*Workflow, error) { + if plantUML == nil { + return d, errors.New("no file available to export") + } + defer plantUML.Close() + + d.Datas = []string{} + d.Storages = []string{} + d.Processings = []string{} + d.Computes = []string{} + d.Workflows = []string{} + + d.DataResources = []*resources.DataResource{} + d.StorageResources = []*resources.StorageResource{} + d.ProcessingResources = []*resources.ProcessingResource{} + d.ComputeResources = []*resources.ComputeResource{} + d.WorkflowResources = []*resources.WorkflowResource{} + + d.Graph = graph.NewGraph() + resourceCatalog := map[string]func() resources.ResourceInterface{ + "Processing": func() resources.ResourceInterface { + return &resources.ProcessingResource{ + AbstractInstanciatedResource: resources.AbstractInstanciatedResource[*resources.ProcessingInstance]{ + Instances: []*resources.ProcessingInstance{}, + }, + } + }, + "Storage": func() resources.ResourceInterface { + return &resources.StorageResource{ + AbstractInstanciatedResource: resources.AbstractInstanciatedResource[*resources.StorageResourceInstance]{ + Instances: []*resources.StorageResourceInstance{}, + }, + } + }, + "Data": func() resources.ResourceInterface { + return &resources.DataResource{ + AbstractInstanciatedResource: resources.AbstractInstanciatedResource[*resources.DataInstance]{ + Instances: []*resources.DataInstance{}, + }, + } + }, + "ComputeUnit": func() resources.ResourceInterface { + return &resources.ComputeResource{ + AbstractInstanciatedResource: resources.AbstractInstanciatedResource[*resources.ComputeResourceInstance]{ + Instances: []*resources.ComputeResourceInstance{}, + }, + } + }, + } + graphVarName := map[string]*graph.GraphItem{} + scanner := bufio.NewScanner(plantUML) + + for scanner.Scan() { + line := scanner.Text() + for n, new := range resourceCatalog { + if strings.Contains(line, n+"(") && !strings.Contains(line, "!procedure") { // should exclude declaration of type. + newRes := new() + varName, graphItem, err := d.extractResourcePlantUML(line, newRes, n, request.PeerID) + if err != nil { + return d, err + } + graphVarName[varName] = graphItem + continue + } else if strings.Contains(line, n+"-->") { + err := d.extractLink(line, graphVarName, "-->") + if err != nil { + fmt.Println(err) + continue + } + } else if strings.Contains(line, n+"--") { + err := d.extractLink(line, graphVarName, "--") + if err != nil { + fmt.Println(err) + continue + } + } else if strings.Contains(line, n+"-") { + err := d.extractLink(line, graphVarName, "-") + if err != nil { + fmt.Println(err) + continue + } + } + } + } + if err := scanner.Err(); err != nil { + return d, err + } + d.generateResource(d.GetResources(tools.DATA_RESOURCE), request) + d.generateResource(d.GetResources(tools.PROCESSING_RESOURCE), request) + d.generateResource(d.GetResources(tools.STORAGE_RESOURCE), request) + d.generateResource(d.GetResources(tools.COMPUTE_RESOURCE), request) + d.generateResource(d.GetResources(tools.WORKFLOW_RESOURCE), request) + return d, nil +} + +func (d *Workflow) generateResource(datas []resources.ResourceInterface, request *tools.APIRequest) error { + for _, d := range datas { + access := d.GetAccessor(request) + if _, code, err := access.LoadOne(d.GetID()); err != nil && code == 200 { + continue + } + access.StoreOne(d) + } + return nil +} + +func (d *Workflow) extractLink(line string, graphVarName map[string]*graph.GraphItem, pattern string) error { + splitted := strings.Split(line, pattern) + if len(splitted) < 2 { + return errors.New("links elements not found") + } + if graphVarName[splitted[0]] != nil { + return errors.New("links elements not found -> " + strings.Trim(splitted[0], " ")) + } + if graphVarName[splitted[1]] != nil { + return errors.New("links elements not found -> " + strings.Trim(splitted[1], " ")) + } + link := &graph.GraphLink{ + Source: graph.Position{ + ID: graphVarName[splitted[0]].ID, + X: 0, + Y: 0, + }, + Destination: graph.Position{ + ID: graphVarName[splitted[1]].ID, + X: 0, + Y: 0, + }, + } + splittedComments := strings.Split(line, "'") + if len(splittedComments) <= 1 { + return errors.New("Can't deserialize Object, there's no commentary") + } + comment := strings.ReplaceAll(splittedComments[1], "'", "") // for now it's a json. + json.Unmarshal([]byte(comment), link) + d.Graph.Links = append(d.Graph.Links, *link) + return nil +} + +func (d *Workflow) extractResourcePlantUML(line string, resource resources.ResourceInterface, dataName string, peerID string) (string, *graph.GraphItem, error) { + splittedFunc := strings.Split(line, "(") + if len(splittedFunc) <= 1 { + return "", nil, errors.New("Can't deserialize Object, there's no func") + } + splittedParams := strings.Split(splittedFunc[1], ",") + if len(splittedFunc) <= 1 { + return "", nil, errors.New("Can't deserialize Object, there's no params") + } + + varName := splittedParams[0] + splitted := strings.Split(splittedParams[1], "\"") + + if len(splitted) <= 1 { + return "", nil, errors.New("Can't deserialize Object, there's no name") + } + resource.SetName(splitted[1]) + + splittedComments := strings.Split(line, "'") + if len(splittedComments) <= 1 { + return "", nil, errors.New("Can't deserialize Object, there's no commentary") + } + comment := strings.ReplaceAll(splittedComments[1], "'", "") // for now it's a json. + instance := d.getNewInstance(dataName, splitted[1], peerID) + if instance == nil { + return "", nil, errors.New("No instance found.") + } + resource.AddInstances(instance) + + json.Unmarshal([]byte(comment), instance) + // deserializer les instances... une instance doit par défaut avoir certaines valeurs d'accès. + graphID := uuid.New() + graphItem := &graph.GraphItem{ + ID: graphID.String(), + } + graphItem = d.getNewGraphItem(dataName, graphItem, resource) + + d.Graph.Items[graphID.String()] = *graphItem + return varName, graphItem, nil +} + +func (d *Workflow) getNewGraphItem(dataName string, graphItem *graph.GraphItem, resource resources.ResourceInterface) *graph.GraphItem { + switch dataName { + case "Data": + d.Datas = append(d.Datas, resource.GetID()) + d.DataResources = append(d.DataResources, resource.(*resources.DataResource)) + graphItem.Data = resource.(*resources.DataResource) + case "Processing", "Event": + d.Processings = append(d.Processings, resource.GetID()) + d.ProcessingResources = append(d.ProcessingResources, resource.(*resources.ProcessingResource)) + graphItem.Processing = resource.(*resources.ProcessingResource) + case "Storage": + d.Storages = append(d.Storages, resource.GetID()) + d.StorageResources = append(d.StorageResources, resource.(*resources.StorageResource)) + graphItem.Storage = resource.(*resources.StorageResource) + case "ComputeUnit": + d.Computes = append(d.Computes, resource.GetID()) + d.ComputeResources = append(d.ComputeResources, resource.(*resources.ComputeResource)) + graphItem.Compute = resource.(*resources.ComputeResource) + default: + return graphItem + } + return graphItem +} + +func (d *Workflow) getNewInstance(dataName string, name string, peerID string) resources.ResourceInstanceITF { + switch dataName { + case "Data": + return resources.NewDataInstance(name, peerID) + case "Processing": + return resources.NewProcessingInstance(name, peerID) + case "Storage": + return resources.NewStorageResourceInstance(name, peerID) + case "ComputeUnit": + return resources.NewComputeResourceInstance(name, peerID) + default: + return nil + } +} + type Deps struct { Source string Dest string