Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion clients/go/db/cmdb_polytype.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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])
}
Expand All @@ -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))
Expand Down
75 changes: 63 additions & 12 deletions docs/type_composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)

---
Expand Down Expand Up @@ -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": "<target object ID>",
"from_super_type":"<superTypeA>",
"to_super_type": "<superTypeB>",
"name": "<linkName>",
"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`
Expand Down Expand Up @@ -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
Expand All @@ -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"}}'
80 changes: 80 additions & 0 deletions embedded/graph/crud/hl_polytype_crud.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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))
Expand All @@ -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,
Expand All @@ -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()
Expand Down Expand Up @@ -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))
Expand Down
Loading