From fb7a71220d73372c468386304ace83c2583a78ce Mon Sep 17 00:00:00 2001 From: atauov Date: Thu, 7 May 2026 14:43:17 +0500 Subject: [PATCH] Add supertype update function --- clients/go/db/cmdb_polytype.go | 27 ++++++++- docs/type_composition.md | 75 +++++++++++++++++++---- embedded/graph/crud/hl_polytype_crud.go | 80 +++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 13 deletions(-) diff --git a/clients/go/db/cmdb_polytype.go b/clients/go/db/cmdb_polytype.go index e21ae4a0..f5440b4e 100644 --- a/clients/go/db/cmdb_polytype.go +++ b/clients/go/db/cmdb_polytype.go @@ -5,6 +5,7 @@ import ( "github.com/foliagecp/sdk/statefun" sfMediators "github.com/foliagecp/sdk/statefun/mediator" sfp "github.com/foliagecp/sdk/statefun/plugins" + "github.com/foliagecp/sdk/statefun/system" ) func (cmdb CMDBSyncClient) TypeSetSubType(baseType, childType string) error { @@ -27,12 +28,15 @@ func (cmdb CMDBSyncClient) TypeRemoveSubType(baseType, childType string) error { func (cmdb CMDBSyncClient) ObjectsLinkSuperTypeCreate(from, to, fromClaimType, toClaimType, name string, tags []string, body ...easyjson.JSON) error { payload := easyjson.NewJSONObject() + payload.SetByPath("op_time", easyjson.NewJSON(system.GetCurrentTimeNs())) payload.SetByPath("to", easyjson.NewJSON(to)) payload.SetByPath("from_super_type", easyjson.NewJSON(fromClaimType)) payload.SetByPath("to_super_type", easyjson.NewJSON(toClaimType)) - payload.SetByPath("name", easyjson.NewJSON(name)) payload.SetByPath("body", easyjson.NewJSONObject()) + if len(name) > 0 { + payload.SetByPath("name", easyjson.NewJSON(name)) + } if len(body) > 0 { payload.SetByPath("body", body[0]) } @@ -45,8 +49,29 @@ func (cmdb CMDBSyncClient) ObjectsLinkSuperTypeCreate(from, to, fromClaimType, t return OpErrorFromOpMsg(sfMediators.OpMsgFromSfReply(cmdb.request(sfp.AutoRequestSelect, "functions.cmdb.api.objects.link.supertype.create", from, &payload, &options))) } +func (cmdb CMDBSyncClient) ObjectsLinkSuperTypeUpdate(from, to, fromClaimType, toClaimType, name string, tags []string, body easyjson.JSON, replace bool) error { + payload := easyjson.NewJSONObject() + payload.SetByPath("op_time", easyjson.NewJSON(system.GetCurrentTimeNs())) + payload.SetByPath("to", easyjson.NewJSON(to)) + payload.SetByPath("from_super_type", easyjson.NewJSON(fromClaimType)) + payload.SetByPath("to_super_type", easyjson.NewJSON(toClaimType)) + payload.SetByPath("body", body) + payload.SetByPath("replace", easyjson.NewJSON(replace)) + if len(name) > 0 { + payload.SetByPath("name", easyjson.NewJSON(name)) + } + if len(tags) > 0 { + payload.SetByPath("tags", easyjson.NewJSON(tags)) + } + + options := easyjson.NewJSONObject() + options.SetByPath(statefun.ShadowObjectCallParamOptionPath, easyjson.NewJSON(cmdb.ShadowObjectCanBeRecevier)) + return OpErrorFromOpMsg(sfMediators.OpMsgFromSfReply(cmdb.request(sfp.AutoRequestSelect, "functions.cmdb.api.objects.link.supertype.update", seqFree(from), &payload, &options))) +} + func (cmdb CMDBSyncClient) ObjectsLinkSuperTypeDelete(from, to, fromClaimType, toClaimType string) error { payload := easyjson.NewJSONObject() + payload.SetByPath("op_time", easyjson.NewJSON(system.GetCurrentTimeNs())) payload.SetByPath("to", easyjson.NewJSON(to)) payload.SetByPath("from_super_type", easyjson.NewJSON(fromClaimType)) payload.SetByPath("to_super_type", easyjson.NewJSON(toClaimType)) diff --git a/docs/type_composition.md b/docs/type_composition.md index 96f43a67..e16a9526 100644 --- a/docs/type_composition.md +++ b/docs/type_composition.md @@ -8,6 +8,7 @@ - [Implementation Details](#implementation-details) - [API Reference](#api-reference) - [Create SuperType-Based Object Link](#create-supertype-based-object-link) + - [Update SuperType-Based Object Link](#update-supertype-based-object-link) - [Delete SuperType-Based Object Link](#delete-supertype-based-object-link) - [Set SubType Relation](#set-subtype-relation) - [Remove SubType Relation](#remove-subtype-relation) @@ -24,6 +25,7 @@ - [Set SubType](#set-subtype) - [Remove SubType](#remove-subtype) - [Create SuperType-Based Object Link](#create-supertype-based-object-link-1) +- [Update SuperType-Based Object Link](#update-supertype-based-object-link-1) - [Delete SuperType-Based Object Link](#delete-supertype-based-object-link-1) --- @@ -102,6 +104,35 @@ For each high-level operation, you can invoke via NATS CLI or via the Go client err := cmdb.ObjectsLinkSuperTypeCreate(from, to, fromClaimType, toClaimType, linkName, tags, payload) ``` +### Update SuperType-Based Object Link + +Updates a supertype-based object link if it exists, or creates it if it does not. Body update semantics match `ObjectsLinkUpdate`: `replace=false` merges the provided body into the existing link body, while `replace=true` replaces the body. + +* **NATS Subject**: `functions.cmdb.api.objects.link.supertype.update` +* **Go Client**: `cmdb.ObjectsLinkSuperTypeUpdate(from, to, fromClaimType, toClaimType, name, tags, body, replace)` +* **NATS Payload**: + + ```json + { + "to": "", + "from_super_type":"", + "to_super_type": "", + "name": "", + "body": { /* optional JSON */ }, + "tags": [ /* optional tags */ ], + "replace": false + } + ``` + + `name` is optional and defaults to the target object ID. The operation always uses upsert semantics. + +* **Go Payload Construction**: + + ```go + body := easyjson.NewJSONObjectWithKeyValue("state", easyjson.NewJSON("updated")) + err := cmdb.ObjectsLinkSuperTypeUpdate(from, to, fromClaimType, toClaimType, linkName, tags, body, false) + ``` + ### Delete SuperType-Based Object Link * **NATS Subject**: `functions.cmdb.api.objects.link.supertype.delete` @@ -248,17 +279,32 @@ cmdb.TypesLinkDelete("typeA","typeB") ## NATS CLI Examples ```bash -# Create subtype relationship -nats req request.hub.functions.cmdb.api.type.subtype.set '{"payload":{"sub_type":"typeC"}}' +# Create the types used by the example +nats req request.hub.functions.cmdb.api.type.create.typeA '{"payload":{"body":{}}}' +nats req request.hub.functions.cmdb.api.type.create.typeB '{"payload":{"body":{}}}' +nats req request.hub.functions.cmdb.api.type.create.typeC '{"payload":{"body":{}}}' -# Remove subtype relationship -nats req request.hub.functions.cmdb.api.type.subtype.remove '{"payload":{"sub_type":"typeC"}}' +# Create subtype relationship: typeC is a subtype of typeA +nats req request.hub.functions.cmdb.api.type.subtype.set.typeA '{"payload":{"sub_type":"typeC"}}' + +# Allow object links from typeA to typeB +nats req request.hub.functions.cmdb.api.types.link.create.typeA '{"payload":{"to":"typeB","object_type":"typeA-typeB"}}' -# Create supertype-based object link -nats req request.hub.functions.cmdb.api.objects.link.supertype.create '{"to":"b","from_super_type":"typeA","to_super_type":"typeB","name":"linkCB","body":{},"tags":[]}' +# Create objects: c has typeC, b has typeB +nats req request.hub.functions.cmdb.api.object.create.c '{"payload":{"origin_type":"typeC","body":{}}}' +nats req request.hub.functions.cmdb.api.object.create.b '{"payload":{"origin_type":"typeB","body":{}}}' + +# Create supertype-based object link c -> b through claimed types typeA -> typeB +nats req request.hub.functions.cmdb.api.objects.link.supertype.create.c '{"payload":{"to":"b","from_super_type":"typeA","to_super_type":"typeB","name":"linkCB","body":{},"tags":[]}}' + +# Update or create the same supertype-based object link +nats req request.hub.functions.cmdb.api.objects.link.supertype.update.c '{"payload":{"to":"b","from_super_type":"typeA","to_super_type":"typeB","name":"linkCB","body":{"cb_state":"updated"},"replace":false}}' # Delete supertype-based object link -nats req request.hub.functions.cmdb.api.objects.link.supertype.delete '{"to":"b","from_super_type":"typeA","to_super_type":"typeB"}' +nats req request.hub.functions.cmdb.api.objects.link.supertype.delete.c '{"payload":{"to":"b","from_super_type":"typeA","to_super_type":"typeB"}}' + +# Remove subtype relationship +nats req request.hub.functions.cmdb.api.type.subtype.remove.typeA '{"payload":{"sub_type":"typeC"}}' ``` ## Go Client API Examples @@ -268,23 +314,28 @@ nats req request.hub.functions.cmdb.api.objects.link.supertype.delete '{"to":"b" cmdb.TypeSetSubType("typeA", "typeC") cmdb.TypeRemoveSubType("typeA", "typeC") -// Create and delete supertype-based object link +// Create, update, and delete supertype-based object link cmdb.ObjectsLinkSuperTypeCreate("c", "b", "typeA", "typeB", "linkCB", nil, payloadCB) +cmdb.ObjectsLinkSuperTypeUpdate("c", "b", "typeA", "typeB", "linkCB", nil, easyjson.NewJSONObjectWithKeyValue("cb_state", easyjson.NewJSON("updated")), false) cmdb.ObjectsLinkSuperTypeDelete("c", "b", "typeA", "typeB") ``` # Set SubType -nats req request.hub.functions.cmdb.api.type.subtype.set '{"payload":{"parent":"typeA","sub\_type":"typeC"}}' +nats req request.hub.functions.cmdb.api.type.subtype.set.typeA '{"payload":{"sub_type":"typeC"}}' # Remove SubType -nats req request.hub.functions.cmdb.api.type.subtype.remove '{"payload":{"parent":"typeA","sub\_type":"typeC"}}' +nats req request.hub.functions.cmdb.api.type.subtype.remove.typeA '{"payload":{"sub_type":"typeC"}}' # Create SuperType-Based Object Link -nats req request.hub.functions.cmdb.api.objects.link.supertype.create '{"payload": {"objA":"c","objB":"b","superTypeA":"typeA","superTypeB":"typeB","linkName":"linkCB","opts"\:null,"payload":{"cb\_state":"created"}} }' +nats req request.hub.functions.cmdb.api.objects.link.supertype.create.c '{"payload":{"to":"b","from_super_type":"typeA","to_super_type":"typeB","name":"linkCB","body":{"cb_state":"created"}}}' + +# Update SuperType-Based Object Link + +nats req request.hub.functions.cmdb.api.objects.link.supertype.update.c '{"payload":{"to":"b","from_super_type":"typeA","to_super_type":"typeB","name":"linkCB","body":{"cb_state":"updated"},"replace":false}}' # Delete SuperType-Based Object Link -nats req request.hub.functions.cmdb.api.objects.link.supertype.delete '{"payload": {"objA":"c","objB":"b","superTypeA":"typeA","superTypeB":"typeB","linkName":"linkCB"} }' +nats req request.hub.functions.cmdb.api.objects.link.supertype.delete.c '{"payload":{"to":"b","from_super_type":"typeA","to_super_type":"typeB"}}' diff --git a/embedded/graph/crud/hl_polytype_crud.go b/embedded/graph/crud/hl_polytype_crud.go index dd092b78..a48681ea 100644 --- a/embedded/graph/crud/hl_polytype_crud.go +++ b/embedded/graph/crud/hl_polytype_crud.go @@ -16,6 +16,7 @@ const ( func RegisterPolyTypeFunctions(runtime *statefun.Runtime) { // High-Level Type Inheritance statefun.NewFunctionType(runtime, "functions.cmdb.api.objects.link.supertype.create", CreateObjectsLinkFromSuperTypes, *statefun.NewFunctionTypeConfig().SetAllowedRequestProviders(sfPlugins.AutoRequestSelect)) + statefun.NewFunctionType(runtime, "functions.cmdb.api.objects.link.supertype.update", UpdateObjectsLinkFromSuperTypes, *statefun.NewFunctionTypeConfig().SetAllowedRequestProviders(sfPlugins.AutoRequestSelect)) statefun.NewFunctionType(runtime, "functions.cmdb.api.objects.link.supertype.delete", DeleteObjectsLinkFromSuperTypes, *statefun.NewFunctionTypeConfig().SetAllowedRequestProviders(sfPlugins.AutoRequestSelect)) statefun.NewFunctionType(runtime, "functions.cmdb.api.type.subtype.set", TypeSetSubType, *statefun.NewFunctionTypeConfig().SetAllowedRequestProviders(sfPlugins.AutoRequestSelect)) statefun.NewFunctionType(runtime, "functions.cmdb.api.type.subtype.remove", TypeRemoveSubType, *statefun.NewFunctionTypeConfig().SetAllowedRequestProviders(sfPlugins.AutoRequestSelect)) @@ -116,6 +117,8 @@ create object -> object link */ func CreateObjectsLinkFromSuperTypes(_ sfPlugins.StatefunExecutor, ctx *sfPlugins.StatefunContextProcessor) { selfID := getOriginalID(ctx.Self.ID) + opTime := getOpTimeFromPayloadIfExist(ctx.Payload) + om := sfMediators.NewOpMediator(ctx) objectToID, ok := ctx.Payload.GetByPath("to").AsString() @@ -153,6 +156,7 @@ func CreateObjectsLinkFromSuperTypes(_ sfPlugins.StatefunExecutor, ctx *sfPlugin if ctx.Payload.PathExists("tags") { objectLink.SetByPath("tags", ctx.Payload.GetByPath("tags")) } + objectLink.SetByPath("op_time", easyjson.NewJSON(opTime)) options := ctx.Options.Clone() options.SetByPath("op_stack", easyjson.NewJSON(true)) @@ -162,6 +166,79 @@ func CreateObjectsLinkFromSuperTypes(_ sfPlugins.StatefunExecutor, ctx *sfPlugin replyWithoutOpStack(om, ctx) } +/* + { + "to": string, + "from_super_type": string, + "to_super_type": string, + "name": string, // optional; defaults to "to" ID + "body": json, + "tags": []string, + "replace": bool, // false (default) = DeepMerge; true = replace + re-index + } + +Always upserts: creates the link if it does not exist. +*/ +func UpdateObjectsLinkFromSuperTypes(_ sfPlugins.StatefunExecutor, ctx *sfPlugins.StatefunContextProcessor) { + selfID := getOriginalID(ctx.Self.ID) + + opTime := getOpTimeFromPayloadIfExist(ctx.Payload) + + om := sfMediators.NewOpMediator(ctx) + + objectToID, ok := ctx.Payload.GetByPath("to").AsString() + if !ok { + om.AggregateOpMsg(sfMediators.OpMsgFailed("'to' undefined")).Reply() + return + } + objectToID = ctx.Domain.CreateObjectIDWithThisDomain(objectToID, false) + + linkName := ctx.Payload.GetByPath("name").AsStringDefault(objectToID) + + fromObjectClaimType := ctx.Payload.GetByPath("from_super_type").AsStringDefault("") + toObjectClaimType := ctx.Payload.GetByPath("to_super_type").AsStringDefault("") + + fromObjectClaimType = ctx.Domain.CreateObjectIDWithHubDomain(fromObjectClaimType, true) + toObjectClaimType = ctx.Domain.CreateObjectIDWithHubDomain(toObjectClaimType, true) + + operationKeysMutexLock(ctx, []string{selfID, objectToID}, true) + + objectLinkType := isObjectLinkPermittedForClaimedTypes(ctx, selfID, objectToID, fromObjectClaimType, toObjectClaimType) + if len(objectLinkType) == 0 { + operationKeysMutexUnlock(ctx) + om.AggregateOpMsg(sfMediators.OpMsgFailed(fmt.Sprintf("no object link from type %s to type %s", fromObjectClaimType, toObjectClaimType))).Reply() + return + } + + finalLinkType := ctx.Domain.GetObjectIDWithoutDomain(fromObjectClaimType) + "#" + ctx.Domain.GetObjectIDWithoutDomain(toObjectClaimType) + "#" + objectLinkType + + objectLink := easyjson.NewJSONObject() + objectLink.SetByPath("to", easyjson.NewJSON(objectToID)) + objectLink.SetByPath("type", easyjson.NewJSON(finalLinkType)) + objectLink.SetByPath("name", easyjson.NewJSON(linkName)) + objectLink.SetByPath("body", ctx.Payload.GetByPath("body")) + objectLink.SetByPath("upsert", easyjson.NewJSON(true)) + if ctx.Payload.PathExists("tags") { + objectLink.SetByPath("tags", ctx.Payload.GetByPath("tags")) + } + if ctx.Payload.PathExists("replace") { + objectLink.SetByPath("replace", ctx.Payload.GetByPath("replace")) + } + objectLink.SetByPath("op_time", easyjson.NewJSON(opTime)) + + options := ctx.Options.Clone() + options.SetByPath("op_stack", easyjson.NewJSON(true)) + om.AggregateOpMsg(sfMediators.OpMsgFromSfReply(ctx.Request(sfPlugins.AutoRequestSelect, "functions.graph.api.link.update", makeSequenceFreeParentBasedID(ctx, selfID), injectParentHoldsLocks(ctx, &objectLink), &options))) + operationKeysMutexUnlock(ctx) + + if om.GetLastSyncOp().Data.PathExists("op_stack") { + j := om.GetLastSyncOp().Data.GetByPathPtr("op_stack") + executeTriggersFromLLOpStack(ctx, j, "", "") + } + + replyWithoutOpStack(om, ctx) +} + /* { "to": string, @@ -171,6 +248,8 @@ func CreateObjectsLinkFromSuperTypes(_ sfPlugins.StatefunExecutor, ctx *sfPlugin */ func DeleteObjectsLinkFromSuperTypes(_ sfPlugins.StatefunExecutor, ctx *sfPlugins.StatefunContextProcessor) { selfID := getOriginalID(ctx.Self.ID) + opTime := getOpTimeFromPayloadIfExist(ctx.Payload) + om := sfMediators.NewOpMediator(ctx) objectToID, ok := ctx.Payload.GetByPath("to").AsString() @@ -200,6 +279,7 @@ func DeleteObjectsLinkFromSuperTypes(_ sfPlugins.StatefunExecutor, ctx *sfPlugin objectLink := easyjson.NewJSONObject() objectLink.SetByPath("to", easyjson.NewJSON(objectToID)) objectLink.SetByPath("type", easyjson.NewJSON(finalLinkType)) + objectLink.SetByPath("op_time", easyjson.NewJSON(opTime)) options := ctx.Options.Clone() options.SetByPath("op_stack", easyjson.NewJSON(true))