Skip to content

Commit

Permalink
Updated spec, doc and implementation to the latest proposal
Browse files Browse the repository at this point in the history
  • Loading branch information
aleksmaus committed Mar 21, 2024
1 parent 97fa1fe commit 2c29824
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 163 deletions.
29 changes: 14 additions & 15 deletions pkg/ottl/ottlfuncs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,32 +247,31 @@ Examples:

### rename

`rename(map, field, targetField, Optional[ignore_missing], Optional[conflict_strategy] )`
`rename(target, source_map, source_key, [Optional] ignore_missing = true, [Optional] conflict_strategy = upsert)`

The `rename` function renames the `map` `field` to `targetField`.

`map` is a path expression to a `pcommon.Map` type field. `field` is a string value of the `map` key that is being renamed. `targetField` is a string value that the `field` is renamed to.
The `rename` function combines `set` and `delete_key` calls in one function. It creates the `target` field with the `source_key` value from `source_map` and it deletes the `source_key` from `source_map`.

This combines what previously required two calls of `set` following with `delete_key` in one function call.
`target` is a path expression to a telemetry field. `source_map` is a path expression to a `pcommon.Map` type field. `source_key` is a string key for `source_map`.

How the ```rename``` function behaves is controlled by the optional `ignore_missing` and `conflict_strategy` arguments.

`ignore_missing` is optional boolean argument, that specifies what happens when the `field` is missing from the `map`. It is set to `true` by default.
- The `true` value results in no changes to the `map` if the `field` key doesn't exists in the map
- The `false` value results in error if the `field` key doesn't exists in the map
`ignore_missing` is optional boolean argument that specifies what happens when the `source_key` is missing from the `source_map`. It is set to `true` by default.
- The `true` value results in no changes if the `source_key` key doesn't exists in the `source_map`
- The `false` value results in error if the `source_key` key doesn't exists in the `source_map`


`conflict_strategy` is an optional string paramater that specifies the conflict resolution strategy for the `targetField`.
Valid values are `replace`, `fail`, and `ignore`. By default, it is set to `replace`.
- The `replace` overwrites the `targetField` if it is already present.
- The `fail` returns an error if `targetField` is already present.
- The `ignore` results in no changes to the `map` if `targetField` is already present.
`conflict_strategy` is an optional string parameter that specifies the conflict resolution strategy for the `target`.
Valid values are `upsert`, `fail`, and `insert`. By default, it is set to `upsert`.
- The `upsert` overwrites the `target` value if it is already present.
- The `fail` returns an error if `target` is already present.
- The `insert` results in no changes if `target` is already present.

Examples:

- `rename(attributes, "foo", "bar")`
- `rename(attributes, "foo", "bar", false)`
- `rename(attributes, "foo", "bar", true, "ignore")`
- `rename(attributes["destination"], attributes, "source")`
- `rename(attributes["destination"], attributes, "source", false)`
- `rename(attributes["destination"], attributes, "source", true, "insert")`

### replace_all_matches

Expand Down
75 changes: 41 additions & 34 deletions pkg/ottl/ottlfuncs/func_rename.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,17 @@ var (
)

const (
renameConflictReplace = "replace"
renameConflictFail = "fail"
renameConflictIgnore = "ignore"
renameConflictInsert = "insert"
renameConflictUpsert = "upsert"
renameConflictFail = "fail"
)

// rename(map, field, target_field, [Optional] ignore_missing = true, [Optional] conflict_strategy = replace)
// rename(target, source_map, source_key, [Optional] ignore_missing = true, [Optional] conflict_strategy = upsert)
type RenameArguments[K any] struct {
Map ottl.PMapGetter[K]
Field string
TargetField string
Target ottl.GetSetter[K]
SourceMap ottl.PMapGetter[K]
SourceKey string

IgnoreMissing ottl.Optional[bool]
ConflictStrategy ottl.Optional[string]
}
Expand All @@ -43,23 +44,24 @@ func createRenameFunction[K any](_ ottl.FunctionContext, oArgs ottl.Arguments) (
return nil, fmt.Errorf("RenameFactory args must be of type *RenameArguments[K]")
}

return rename(args.Map, args.Field, args.TargetField, args.IgnoreMissing, args.ConflictStrategy)
return rename(args.Target, args.SourceMap, args.SourceKey, args.IgnoreMissing, args.ConflictStrategy)
}

func rename[K any](mg ottl.PMapGetter[K], f string, tf string, im ottl.Optional[bool], cs ottl.Optional[string]) (ottl.ExprFunc[K], error) {
conflictStrategy := renameConflictReplace
func rename[K any](target ottl.GetSetter[K], sm ottl.PMapGetter[K], sourceKey string,
im ottl.Optional[bool], cs ottl.Optional[string]) (ottl.ExprFunc[K], error) {
conflictStrategy := renameConflictUpsert
if !cs.IsEmpty() {
conflictStrategy = cs.Get()
}

if conflictStrategy != renameConflictReplace &&
conflictStrategy != renameConflictFail &&
conflictStrategy != renameConflictIgnore {
return nil, fmt.Errorf("%v %w, must be %q, %q or %q", conflictStrategy, ErrRenameInvalidConflictStrategy, renameConflictReplace, renameConflictFail, renameConflictIgnore)
if conflictStrategy != renameConflictInsert &&
conflictStrategy != renameConflictUpsert &&
conflictStrategy != renameConflictFail {
return nil, fmt.Errorf("%v %w, must be %q, %q or %q", conflictStrategy, ErrRenameInvalidConflictStrategy, renameConflictInsert, renameConflictFail, renameConflictUpsert)
}

return func(ctx context.Context, tCtx K) (any, error) {
m, err := mg.Get(ctx, tCtx)
sourceMap, err := sm.Get(ctx, tCtx)
if err != nil {
return nil, err
}
Expand All @@ -70,45 +72,50 @@ func rename[K any](mg ottl.PMapGetter[K], f string, tf string, im ottl.Optional[
ignoreMissing = im.Get()
}

val, exists := m.Get(f)
// Get value from source_map
sourceVal, sourceExists := sourceMap.Get(sourceKey)

// Apply ignore_missing to the source
if !exists {
if !sourceExists {
if ignoreMissing {
// If ignore missing return
// If ignore_missing is true, return
return nil, nil
}
return nil, fmt.Errorf("%v %w, while ignore_missing is false", f, ErrRenameKeyIsMissing)
return nil, fmt.Errorf("%v %w, while ignore_missing is false", sourceKey, ErrRenameKeyIsMissing)
}

// Apply conflict_strategy to the target
_, oldExists := m.Get(tf)
oldVal, err := target.Get(ctx, tCtx)
if err != nil {
return nil, err
}

oldExists := (oldVal != nil)

switch conflictStrategy {
case renameConflictReplace:
case renameConflictInsert:
// Noop if target field present
if oldExists {
return nil, nil
}
case renameConflictUpsert:
// Overwrite if present or create when missing
case renameConflictFail:
// Fail if target field present
if oldExists {
return nil, ErrRenameKeyAlreadyExists
}
case renameConflictIgnore:
// Noop if target field present
if oldExists {
return nil, nil
}
}

// If field and targetField are the same
if f == tf {
return nil, nil
}
// Save raw value, since the sourceVal gets modified when the key is removed
rawVal := sourceVal.AsRaw()

// Remove field from source
sourceMap.Remove(sourceKey)

// Copy field value to targetField
val.CopyTo(m.PutEmpty(tf))
// Set value to target
target.Set(ctx, tCtx, rawVal)

// Remove field from map
m.Remove(f)
return nil, nil
}, nil
}
Loading

0 comments on commit 2c29824

Please sign in to comment.