plantuml duplication behavior
This commit is contained in:
+121
-23
@@ -11,6 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"cloud.o-forge.io/core/oc-lib/dbs"
|
||||||
"cloud.o-forge.io/core/oc-lib/models/booking"
|
"cloud.o-forge.io/core/oc-lib/models/booking"
|
||||||
"cloud.o-forge.io/core/oc-lib/models/booking/planner"
|
"cloud.o-forge.io/core/oc-lib/models/booking/planner"
|
||||||
"cloud.o-forge.io/core/oc-lib/models/collaborative_area/shallow_collaborative_area"
|
"cloud.o-forge.io/core/oc-lib/models/collaborative_area/shallow_collaborative_area"
|
||||||
@@ -108,9 +109,9 @@ func (d *Workflow) GetResources(dt tools.DataType) []resources.ResourceInterface
|
|||||||
return itf
|
return itf
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Workflow) ExtractFromPlantUML(plantUML multipart.File, request *tools.APIRequest) (*Workflow, error) {
|
func (d *Workflow) ExtractFromPlantUML(plantUML multipart.File, request *tools.APIRequest) (*Workflow, []string, error) {
|
||||||
if plantUML == nil {
|
if plantUML == nil {
|
||||||
return d, errors.New("no file available to export")
|
return d, nil, errors.New("no file available to export")
|
||||||
}
|
}
|
||||||
|
|
||||||
defer plantUML.Close()
|
defer plantUML.Close()
|
||||||
@@ -189,9 +190,11 @@ func (d *Workflow) ExtractFromPlantUML(plantUML multipart.File, request *tools.A
|
|||||||
lines = append(lines, scanner.Text())
|
lines = append(lines, scanner.Text())
|
||||||
}
|
}
|
||||||
if err := scanner.Err(); err != nil {
|
if err := scanner.Err(); err != nil {
|
||||||
return d, err
|
return d, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var warnings []string
|
||||||
|
|
||||||
for i, line := range lines {
|
for i, line := range lines {
|
||||||
trimmed := strings.TrimSpace(line)
|
trimmed := strings.TrimSpace(line)
|
||||||
|
|
||||||
@@ -237,14 +240,15 @@ func (d *Workflow) ExtractFromPlantUML(plantUML multipart.File, request *tools.A
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for n, new := range resourceCatalog {
|
for n, newFn := range resourceCatalog {
|
||||||
if strings.Contains(line, n+"(") && !strings.Contains(line, "!procedure") && !strings.Contains(line, "!define") {
|
if strings.Contains(line, n+"(") && !strings.Contains(line, "!procedure") && !strings.Contains(line, "!define") {
|
||||||
newRes := new()
|
newRes := newFn()
|
||||||
newRes.SetID(uuid.New().String())
|
newRes.SetID(uuid.New().String())
|
||||||
varName, graphItem, err := d.extractResourcePlantUML(parseLine, newRes, n, request.PeerID)
|
varName, graphItem, warns, err := d.extractResourcePlantUML(parseLine, newRes, n, request.PeerID, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d, err
|
return d, warnings, err
|
||||||
}
|
}
|
||||||
|
warnings = append(warnings, warns...)
|
||||||
if graphItem != nil {
|
if graphItem != nil {
|
||||||
graphVarName[varName] = *graphItem
|
graphVarName[varName] = *graphItem
|
||||||
}
|
}
|
||||||
@@ -258,10 +262,9 @@ func (d *Workflow) ExtractFromPlantUML(plantUML multipart.File, request *tools.A
|
|||||||
d.generateResource(d.GetResources(tools.STORAGE_RESOURCE), request)
|
d.generateResource(d.GetResources(tools.STORAGE_RESOURCE), request)
|
||||||
d.generateResource(d.GetResources(tools.COMPUTE_RESOURCE), request)
|
d.generateResource(d.GetResources(tools.COMPUTE_RESOURCE), request)
|
||||||
d.generateResource(d.GetResources(tools.WORKFLOW_RESOURCE), request)
|
d.generateResource(d.GetResources(tools.WORKFLOW_RESOURCE), request)
|
||||||
d.generateResource(d.GetResources(tools.SERVICE_RESOURCE), request)
|
|
||||||
d.generateResource(d.GetResources(tools.DYNAMIC_RESOURCE), request)
|
d.generateResource(d.GetResources(tools.DYNAMIC_RESOURCE), request)
|
||||||
d.Graph.Items = graphVarName
|
d.Graph.Items = graphVarName
|
||||||
return d, nil
|
return d, warnings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Workflow) generateResource(datas []resources.ResourceInterface, request *tools.APIRequest) error {
|
func (d *Workflow) generateResource(datas []resources.ResourceInterface, request *tools.APIRequest) error {
|
||||||
@@ -402,35 +405,46 @@ func (d *Workflow) extractLink(line string, graphVarName map[string]graph.GraphI
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Workflow) extractResourcePlantUML(line string, resource resources.ResourceInterface, dataName string, peerID string) (string, *graph.GraphItem, error) {
|
func (d *Workflow) extractResourcePlantUML(line string, resource resources.ResourceInterface, dataName string, peerID string, request *tools.APIRequest) (string, *graph.GraphItem, []string, error) {
|
||||||
splittedFunc := strings.Split(line, "(")
|
splittedFunc := strings.Split(line, "(")
|
||||||
if len(splittedFunc) <= 1 {
|
if len(splittedFunc) <= 1 {
|
||||||
return "", nil, errors.New("Can't deserialize Object, there's no func")
|
return "", nil, nil, errors.New("Can't deserialize Object, there's no func")
|
||||||
}
|
}
|
||||||
splittedParams := strings.Split(splittedFunc[1], ",")
|
splittedParams := strings.Split(splittedFunc[1], ",")
|
||||||
if len(splittedParams) <= 1 {
|
if len(splittedParams) <= 1 {
|
||||||
return "", nil, errors.New("Can't deserialize Object, there's no params")
|
return "", nil, nil, errors.New("Can't deserialize Object, there's no params")
|
||||||
}
|
}
|
||||||
|
|
||||||
varName := splittedParams[0]
|
varName := splittedParams[0]
|
||||||
splitted := strings.Split(splittedParams[1], "\"")
|
splitted := strings.Split(splittedParams[1], "\"")
|
||||||
|
|
||||||
if len(splitted) <= 1 {
|
if len(splitted) <= 1 {
|
||||||
return "", nil, errors.New("Can't deserialize Object, there's no name")
|
return "", nil, nil, errors.New("Can't deserialize Object, there's no name")
|
||||||
}
|
}
|
||||||
resource.SetName(strings.ReplaceAll(splitted[1], "\\n", " "))
|
name := strings.ReplaceAll(splitted[1], "\\n", " ")
|
||||||
|
|
||||||
// Resources with instances get a default one seeded from the parent resource,
|
// Extract comment text (if present) for metadata parsing.
|
||||||
// then overridden by any explicit comment attributes.
|
comment := ""
|
||||||
// Event (NativeTool) has no instance: getNewInstance returns nil and is skipped.
|
if parts := strings.Split(line, "'"); len(parts) > 1 {
|
||||||
instance := d.getNewInstance(dataName, splitted[1], peerID)
|
comment = strings.ReplaceAll(parts[1], "'", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
var warns []string
|
||||||
|
|
||||||
|
// Try to resolve an existing catalog resource (by id, then by name).
|
||||||
|
if existing, warn := d.resolveExistingResource(resource, dataName, name, comment, request); existing != nil {
|
||||||
|
warns = append(warns, warn)
|
||||||
|
item := d.addExistingGraphItem(dataName, existing)
|
||||||
|
return varName, item, warns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// No existing resource — create new.
|
||||||
|
resource.SetName(name)
|
||||||
|
instance := d.getNewInstance(dataName, name, peerID)
|
||||||
if instance != nil {
|
if instance != nil {
|
||||||
if b, err := json.Marshal(resource); err == nil {
|
if b, err := json.Marshal(resource); err == nil {
|
||||||
json.Unmarshal(b, instance)
|
json.Unmarshal(b, instance)
|
||||||
}
|
}
|
||||||
splittedComments := strings.Split(line, "'")
|
if comment != "" {
|
||||||
if len(splittedComments) > 1 {
|
|
||||||
comment := strings.ReplaceAll(splittedComments[1], "'", "")
|
|
||||||
json.Unmarshal(parseHumanFriendlyAttrs(comment), instance)
|
json.Unmarshal(parseHumanFriendlyAttrs(comment), instance)
|
||||||
}
|
}
|
||||||
resource.AddInstances(instance)
|
resource.AddInstances(instance)
|
||||||
@@ -441,7 +455,91 @@ func (d *Workflow) extractResourcePlantUML(line string, resource resources.Resou
|
|||||||
d.Graph.Items[item.ID] = *item
|
d.Graph.Items[item.ID] = *item
|
||||||
}
|
}
|
||||||
|
|
||||||
return varName, item, nil
|
return varName, item, warns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolveExistingResource tries to find an existing catalog resource matching
|
||||||
|
// the given name or the id embedded in the PlantUML comment.
|
||||||
|
// Returns (resource, warning message) or (nil, "") if none found.
|
||||||
|
func (d *Workflow) resolveExistingResource(
|
||||||
|
proto resources.ResourceInterface,
|
||||||
|
dataName, name, comment string,
|
||||||
|
request *tools.APIRequest,
|
||||||
|
) (resources.ResourceInterface, string) {
|
||||||
|
accessor := proto.GetAccessor(request)
|
||||||
|
|
||||||
|
// 1. Try lookup by id from comment ("id: <uuid>").
|
||||||
|
if comment != "" {
|
||||||
|
attrs := map[string]any{}
|
||||||
|
json.Unmarshal(parseHumanFriendlyAttrs(comment), &attrs)
|
||||||
|
if id, ok := attrs["id"].(string); ok && id != "" {
|
||||||
|
if dbObj, _, err := accessor.LoadOne(id); err == nil && dbObj != nil {
|
||||||
|
if ri, ok := dbObj.(resources.ResourceInterface); ok {
|
||||||
|
return ri, fmt.Sprintf(`[import warning] %s "%s": existing resource retrieved by id %s`, dataName, name, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Try search by exact name.
|
||||||
|
filter := &dbs.Filters{
|
||||||
|
Or: map[string][]dbs.Filter{
|
||||||
|
"abstractobject.name": {{Operator: dbs.EQUAL.String(), Value: name}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if results, _, err := accessor.Search(filter, "", false, 0, 10); err == nil {
|
||||||
|
for _, r := range results {
|
||||||
|
if r.GetName() == name {
|
||||||
|
if dbObj, _, err2 := accessor.LoadOne(r.GetID()); err2 == nil && dbObj != nil {
|
||||||
|
if ri, ok := dbObj.(resources.ResourceInterface); ok {
|
||||||
|
return ri, fmt.Sprintf(`[import warning] %s "%s": existing resource found by name and retrieved instead of creating a new one`, dataName, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// addExistingGraphItem registers an already-existing resource in the workflow's
|
||||||
|
// ID lists and graph, without adding it to the resource lists (so generateResource
|
||||||
|
// won't try to store it again).
|
||||||
|
func (d *Workflow) addExistingGraphItem(dataName string, resource resources.ResourceInterface) *graph.GraphItem {
|
||||||
|
graphItem := &graph.GraphItem{
|
||||||
|
ID: uuid.New().String(),
|
||||||
|
ItemResource: &resources.ItemResource{},
|
||||||
|
}
|
||||||
|
switch dataName {
|
||||||
|
case "Data":
|
||||||
|
d.Datas = append(d.Datas, resource.GetID())
|
||||||
|
if r, ok := resource.(*resources.DataResource); ok {
|
||||||
|
graphItem.Data = r
|
||||||
|
}
|
||||||
|
case "Processing":
|
||||||
|
d.Processings = append(d.Processings, resource.GetID())
|
||||||
|
if r, ok := resource.(*resources.ProcessingResource); ok {
|
||||||
|
graphItem.Processing = r
|
||||||
|
}
|
||||||
|
case "Service":
|
||||||
|
d.Services = append(d.Services, resource.GetID())
|
||||||
|
if r, ok := resource.(*resources.ServiceResource); ok {
|
||||||
|
graphItem.Service = r
|
||||||
|
}
|
||||||
|
case "Storage":
|
||||||
|
d.Storages = append(d.Storages, resource.GetID())
|
||||||
|
if r, ok := resource.(*resources.StorageResource); ok {
|
||||||
|
graphItem.Storage = r
|
||||||
|
}
|
||||||
|
case "ComputeUnit":
|
||||||
|
d.Computes = append(d.Computes, resource.GetID())
|
||||||
|
if r, ok := resource.(*resources.ComputeResource); ok {
|
||||||
|
graphItem.Compute = r
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return graphItem
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Workflow) getNewGraphItem(dataName string, resource resources.ResourceInterface) *graph.GraphItem {
|
func (d *Workflow) getNewGraphItem(dataName string, resource resources.ResourceInterface) *graph.GraphItem {
|
||||||
|
|||||||
Reference in New Issue
Block a user