Question go-c8y-cli extension

I started a go-c8y-cli extension c8y-mapper to manage mappings, connectors, … in the cumulocity-dynamic-mapper.

The extension feature is really great for these tasks from a command line tool. Thanks …

When developing the extension some question occurred:

  1. I use the API type to define the subcommand mappings for some actions: list, get, .... At the same time I defined activate for the same group mappings as Command as well. Can I use both at the same time? So:
  • c8y mapper mappings activate → command from commands
  • c8y mapper mappings list, ... → command from API
  1. In the command activation I use a c8y pipeline command to update a fragment in a managedObject. Can I achieve the same using the API type?
    So, first retrieve the managedObject and then merge the changes to this retrieved object? I tried to look into jsonnet for this, but got stuck …
  2. I defined a type for type-mapping similar to type-device to complete mapping ids.
    When I use it I get the following error. What could be the reason?
# c8y-mapper % c8y mapper mappings get --id Mapping\ -\ 10
2024-10-14T09:02:43.486+0200    ERROR   commandError: referenceByName: no matching items found. name=Mapping - 10  ::StatusCode=404

Point 1

No, currently mixing API based commands with script based commands is not supported. However I do have it on my todo list as using a yaml definition file would enable being able to provide better completion for script based commands. Currently script based commands don’t get any custom completion as go-c8y-cli as no way of knowing what arguments the script accepts.

Point 2

Technically you can support piping to custom scripts, however you then need to do the pipe detection within your script (which is not trivial to do).

Instead, I would recommend using an API based command, as it seems that you’re just doing a custom inventory update

Though, I noticed there the argument parsing has a few bugs in it. If you’re doing bash style argument parsing, then flags which take a value, e.g. --status <value>, then you need an additional shift to pop the value off the arg stack.

So the argument parsing should be:

while [ $# -gt 0 ]; do
    case "$1" in
        --status)
            STATUS="$2"
            shift
            ;;
		--id)
            ID="$2"
            shift
            ;;
        -h|--help)
            help
            exit 0
            ;;
        --*|-*)
            # Collect flags which will be passed to c8y devices create
            FLAGS+=("$1")
            ;;
    esac
    shift
done

Point 3

The root cause to the “lookup” not working was due to a silent yaml syntax error where the lookup.command item indentation was not consistent leading to the remaining items (everything after c8y) to be ignored. Yaml is similar to python, and whitespace matters, and unfortunately it can fail in very subtle ways.

Fixing the indentation makes everything work again.

Note: I just tested this on the c8y mapper mappings get command by using the yaml anchor <<: *type-mapping reference (rather than using the manually defined completion that you had.)

Before (Broken)

  lookup:
    type: external
    command:
      - c8y
        - inventory
        - find
        - --type
        - "d11r_mapping"
        - --query
        - "name eq '%s*'"
        - --select=id

After (working)

lookup:
    type: external
    command:
      - c8y
      - inventory
      - find
      - --type
      - "d11r_mapping"
      - --query
      - "name eq '%s*'"
      - --select=id
1 Like

I’ve created some PRs to show the fixes and improvements:

1 Like

Thank you for pointing out these bugs & the pull requests.
to Point 2:

Instead, I would recommend using an API based command, as it seems that you’re just doing a custom inventory update …

How can an update in two steps in the API yaml be implemented?
The update of the status needs to send the complete fragment:

  "d11r_mapping": {
    "active": "true",
    ... <- here all remaining d11r_mapping attributes have to be included
  }

Ok, since the inventory update is updating a nested property, then you can’t use an API based command. So you have two options:

  1. Keep with a script based command (my edits also supported reading from pipeline) so it should be ok
  2. Change your data model to decouple the mapping definition with the activation status so it can be enabled/disabled via a single root property instead of a nested one.

However, there might be value in adding a new feature to go-c8y-cli which allows users to do a functional PATCH (client side).

1 Like