From 67713e3cdaa5a70220bcc47c34fbaa25a8ee9a03 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 15 Oct 2024 15:30:36 +0300 Subject: [PATCH 01/88] feat: v3 api support --- .github/workflows/ci.yml | 16 +- .github/workflows/tags.yml | 25 +- .golangci.yml | 15 +- .goreleaser.yml | 59 +++- README.md | 6 +- go.mod | 18 +- go.sum | 36 +-- internal/app/enaptercli/cmd_base.go | 116 ++++++-- internal/app/enaptercli/cmd_blueprints.go | 56 ++++ .../app/enaptercli/cmd_blueprints_download.go | 96 +++++++ .../app/enaptercli/cmd_blueprints_inspect.go | 53 ++++ .../app/enaptercli/cmd_blueprints_upload.go | 68 +++++ internal/app/enaptercli/cmd_devices.go | 51 +++- .../cmd_devices_assign_blueprint.go | 55 ++++ .../app/enaptercli/cmd_devices_execute.go | 119 -------- .../enaptercli/cmd_devices_execute_test.go | 101 ------- internal/app/enaptercli/cmd_devices_logs.go | 112 +++++++- .../app/enaptercli/cmd_devices_logs_test.go | 41 --- internal/app/enaptercli/cmd_devices_logsf.go | 71 +++++ internal/app/enaptercli/cmd_devices_upload.go | 156 ---------- .../app/enaptercli/cmd_devices_upload_logs.go | 74 ----- .../cmd_devices_upload_logs_test.go | 65 ----- .../app/enaptercli/cmd_devices_upload_test.go | 78 ----- internal/app/enaptercli/cmd_logs_test.go | 196 ------------- internal/app/enaptercli/cmd_provisioning.go | 20 ++ .../enaptercli/cmd_provisioning_lua_device.go | 71 +++++ .../enaptercli/cmd_provisioning_standalone.go | 63 ++++ internal/app/enaptercli/cmd_rules.go | 30 -- internal/app/enaptercli/cmd_rules_logs.go | 43 --- .../app/enaptercli/cmd_rules_logs_test.go | 41 --- internal/app/enaptercli/cmd_rules_update.go | 107 ------- .../app/enaptercli/cmd_rules_update_test.go | 74 ----- internal/app/enaptercli/cmd_test.go | 137 --------- internal/app/enaptercli/errors.go | 6 +- internal/app/enaptercli/execute.go | 29 +- internal/app/enaptercli/once_writer.go | 20 -- .../testdata/device_execute/error/output | 1 - .../testdata/device_execute/error/responses | 1 - .../testdata/device_execute/progress/output | 3 - .../device_execute/progress/responses | 3 - .../testdata/device_execute/simple/output | 1 - .../testdata/device_execute/simple/responses | 2 - .../disconnect/device_not_found/input | 5 - .../disconnect/device_not_found/output | 3 - .../disconnect/invalid_token/input | 1 - .../disconnect/invalid_token/output | 1 - .../testdata/device_logs/simple/input | 16 -- .../testdata/device_logs/simple/output | 6 - .../testdata/device_upload/cli_message/output | 9 - .../device_upload/cli_message/requests | 7 - .../device_upload/cli_message/responses | 4 - .../device_upload/cli_message/settings.json | 4 - .../simple/blueprint/manifest.yml | 0 .../testdata/device_upload/simple/output | 8 - .../testdata/device_upload/simple/requests | 7 - .../testdata/device_upload/simple/responses | 4 - .../device_upload/simple/settings.json | 3 - .../device_upload/upload_errors/output | 5 - .../device_upload/upload_errors/requests | 1 - .../device_upload/upload_errors/responses | 1 - .../device_upload/upload_errors/settings.json | 3 - .../device_upload_logs/cli_message/output | 5 - .../device_upload_logs/cli_message/requests | 3 - .../device_upload_logs/cli_message/responses | 2 - .../cli_message/settings.json | 5 - .../testdata/device_upload_logs/simple/output | 4 - .../device_upload_logs/simple/requests | 3 - .../device_upload_logs/simple/responses | 2 - .../device_upload_logs/simple/settings.json | 4 - .../unknown_hardware_id/output | 1 - .../unknown_hardware_id/requests | 1 - .../unknown_hardware_id/responses | 1 - .../unknown_hardware_id/settings.json | 4 - .../output | 1 - .../requests | 1 - .../responses | 1 - .../settings.json | 3 - .../unknown_operation_id/output | 1 - .../unknown_operation_id/requests | 1 - .../unknown_operation_id/responses | 1 - .../unknown_operation_id/settings.json | 4 - .../without_operation_id/output | 8 - .../without_operation_id/requests | 9 - .../without_operation_id/responses | 5 - .../without_operation_id/settings.json | 3 - .../app/enaptercli/testdata/helps/enapter | 13 +- .../testdata/helps/enapter blueprints | 14 + .../helps/enapter blueprints download | 15 + .../testdata/helps/enapter blueprints inspect | 13 + .../testdata/helps/enapter blueprints upload | 13 + .../enaptercli/testdata/helps/enapter devices | 16 +- .../helps/enapter devices assign_blueprint | 15 + .../testdata/helps/enapter devices execute | 16 -- .../testdata/helps/enapter devices logs | 18 +- .../testdata/helps/enapter devices logsf | 14 + .../testdata/helps/enapter devices upload | 18 -- .../helps/enapter devices upload-logs | 15 - .../testdata/helps/enapter provisioning | 13 + .../helps/enapter provisioning lua_device | 16 ++ .../helps/enapter provisioning standalone | 15 + .../testdata/helps/enapter rules logs | 13 - .../testdata/helps/enapter rules update | 17 -- .../rules_logs/disconnect/invalid_token/input | 1 - .../disconnect/invalid_token/output | 1 - .../disconnect/rule_not_found/input | 5 - .../disconnect/rule_not_found/output | 3 - .../testdata/rules_logs/simple/input | 14 - .../testdata/rules_logs/simple/output | 5 - .../testdata/rules_update/errors/output | 3 - .../testdata/rules_update/errors/requests | 1 - .../testdata/rules_update/errors/responses | 1 - .../testdata/rules_update/errors/rule.lua | 1 - .../rules_update/errors/settings.json | 4 - .../testdata/rules_update/simple/output | 1 - .../testdata/rules_update/simple/requests | 1 - .../testdata/rules_update/simple/responses | 1 - .../testdata/rules_update/simple/rule.lua | 1 - .../rules_update/simple/settings.json | 4 - internal/cloudapi/client.go | 76 ----- internal/cloudapi/devices.go | 172 ----------- internal/cloudapi/errors.go | 10 - internal/cloudapi/logs_writer.go | 272 ------------------ internal/cloudapi/rules.go | 67 ----- internal/publichttp/client.go | 105 ------- internal/publichttp/commands.go | 114 -------- internal/publichttp/errors.go | 30 -- internal/publichttp/transport.go | 28 -- 127 files changed, 1050 insertions(+), 2621 deletions(-) create mode 100644 internal/app/enaptercli/cmd_blueprints.go create mode 100644 internal/app/enaptercli/cmd_blueprints_download.go create mode 100644 internal/app/enaptercli/cmd_blueprints_inspect.go create mode 100644 internal/app/enaptercli/cmd_blueprints_upload.go create mode 100644 internal/app/enaptercli/cmd_devices_assign_blueprint.go delete mode 100644 internal/app/enaptercli/cmd_devices_execute.go delete mode 100644 internal/app/enaptercli/cmd_devices_execute_test.go delete mode 100644 internal/app/enaptercli/cmd_devices_logs_test.go create mode 100644 internal/app/enaptercli/cmd_devices_logsf.go delete mode 100644 internal/app/enaptercli/cmd_devices_upload.go delete mode 100644 internal/app/enaptercli/cmd_devices_upload_logs.go delete mode 100644 internal/app/enaptercli/cmd_devices_upload_logs_test.go delete mode 100644 internal/app/enaptercli/cmd_devices_upload_test.go delete mode 100644 internal/app/enaptercli/cmd_logs_test.go create mode 100644 internal/app/enaptercli/cmd_provisioning.go create mode 100644 internal/app/enaptercli/cmd_provisioning_lua_device.go create mode 100644 internal/app/enaptercli/cmd_provisioning_standalone.go delete mode 100644 internal/app/enaptercli/cmd_rules.go delete mode 100644 internal/app/enaptercli/cmd_rules_logs.go delete mode 100644 internal/app/enaptercli/cmd_rules_logs_test.go delete mode 100644 internal/app/enaptercli/cmd_rules_update.go delete mode 100644 internal/app/enaptercli/cmd_rules_update_test.go delete mode 100644 internal/app/enaptercli/cmd_test.go delete mode 100644 internal/app/enaptercli/once_writer.go delete mode 100644 internal/app/enaptercli/testdata/device_execute/error/output delete mode 100644 internal/app/enaptercli/testdata/device_execute/error/responses delete mode 100644 internal/app/enaptercli/testdata/device_execute/progress/output delete mode 100644 internal/app/enaptercli/testdata/device_execute/progress/responses delete mode 100644 internal/app/enaptercli/testdata/device_execute/simple/output delete mode 100644 internal/app/enaptercli/testdata/device_execute/simple/responses delete mode 100644 internal/app/enaptercli/testdata/device_logs/disconnect/device_not_found/input delete mode 100644 internal/app/enaptercli/testdata/device_logs/disconnect/device_not_found/output delete mode 100644 internal/app/enaptercli/testdata/device_logs/disconnect/invalid_token/input delete mode 100644 internal/app/enaptercli/testdata/device_logs/disconnect/invalid_token/output delete mode 100644 internal/app/enaptercli/testdata/device_logs/simple/input delete mode 100644 internal/app/enaptercli/testdata/device_logs/simple/output delete mode 100644 internal/app/enaptercli/testdata/device_upload/cli_message/output delete mode 100644 internal/app/enaptercli/testdata/device_upload/cli_message/requests delete mode 100644 internal/app/enaptercli/testdata/device_upload/cli_message/responses delete mode 100644 internal/app/enaptercli/testdata/device_upload/cli_message/settings.json delete mode 100644 internal/app/enaptercli/testdata/device_upload/simple/blueprint/manifest.yml delete mode 100644 internal/app/enaptercli/testdata/device_upload/simple/output delete mode 100644 internal/app/enaptercli/testdata/device_upload/simple/requests delete mode 100644 internal/app/enaptercli/testdata/device_upload/simple/responses delete mode 100644 internal/app/enaptercli/testdata/device_upload/simple/settings.json delete mode 100644 internal/app/enaptercli/testdata/device_upload/upload_errors/output delete mode 100644 internal/app/enaptercli/testdata/device_upload/upload_errors/requests delete mode 100644 internal/app/enaptercli/testdata/device_upload/upload_errors/responses delete mode 100644 internal/app/enaptercli/testdata/device_upload/upload_errors/settings.json delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/cli_message/output delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/cli_message/requests delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/cli_message/responses delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/cli_message/settings.json delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/simple/output delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/simple/requests delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/simple/responses delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/simple/settings.json delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/output delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/requests delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/responses delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/settings.json delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/output delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/requests delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/responses delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/settings.json delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/output delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/requests delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/responses delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/settings.json delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/output delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/requests delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/responses delete mode 100644 internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/settings.json create mode 100644 internal/app/enaptercli/testdata/helps/enapter blueprints create mode 100644 internal/app/enaptercli/testdata/helps/enapter blueprints download create mode 100644 internal/app/enaptercli/testdata/helps/enapter blueprints inspect create mode 100644 internal/app/enaptercli/testdata/helps/enapter blueprints upload create mode 100644 internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint delete mode 100644 internal/app/enaptercli/testdata/helps/enapter devices execute create mode 100644 internal/app/enaptercli/testdata/helps/enapter devices logsf delete mode 100644 internal/app/enaptercli/testdata/helps/enapter devices upload delete mode 100644 internal/app/enaptercli/testdata/helps/enapter devices upload-logs create mode 100644 internal/app/enaptercli/testdata/helps/enapter provisioning create mode 100644 internal/app/enaptercli/testdata/helps/enapter provisioning lua_device create mode 100644 internal/app/enaptercli/testdata/helps/enapter provisioning standalone delete mode 100644 internal/app/enaptercli/testdata/helps/enapter rules logs delete mode 100644 internal/app/enaptercli/testdata/helps/enapter rules update delete mode 100644 internal/app/enaptercli/testdata/rules_logs/disconnect/invalid_token/input delete mode 100644 internal/app/enaptercli/testdata/rules_logs/disconnect/invalid_token/output delete mode 100644 internal/app/enaptercli/testdata/rules_logs/disconnect/rule_not_found/input delete mode 100644 internal/app/enaptercli/testdata/rules_logs/disconnect/rule_not_found/output delete mode 100644 internal/app/enaptercli/testdata/rules_logs/simple/input delete mode 100644 internal/app/enaptercli/testdata/rules_logs/simple/output delete mode 100644 internal/app/enaptercli/testdata/rules_update/errors/output delete mode 100644 internal/app/enaptercli/testdata/rules_update/errors/requests delete mode 100644 internal/app/enaptercli/testdata/rules_update/errors/responses delete mode 100644 internal/app/enaptercli/testdata/rules_update/errors/rule.lua delete mode 100644 internal/app/enaptercli/testdata/rules_update/errors/settings.json delete mode 100644 internal/app/enaptercli/testdata/rules_update/simple/output delete mode 100644 internal/app/enaptercli/testdata/rules_update/simple/requests delete mode 100644 internal/app/enaptercli/testdata/rules_update/simple/responses delete mode 100644 internal/app/enaptercli/testdata/rules_update/simple/rule.lua delete mode 100644 internal/app/enaptercli/testdata/rules_update/simple/settings.json delete mode 100644 internal/cloudapi/client.go delete mode 100644 internal/cloudapi/devices.go delete mode 100644 internal/cloudapi/errors.go delete mode 100644 internal/cloudapi/logs_writer.go delete mode 100644 internal/cloudapi/rules.go delete mode 100644 internal/publichttp/client.go delete mode 100644 internal/publichttp/commands.go delete mode 100644 internal/publichttp/errors.go delete mode 100644 internal/publichttp/transport.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index edda880..d2320ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,12 +12,12 @@ jobs: steps: - name: Set up Go 1.x - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: ~1.17 + go-version: ~1.21 id: go - name: Check out code into the Go module directory - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Test run: go test -timeout 1m ./... @@ -25,11 +25,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 with: - go-version: ~1.17 + go-version: ~1.21 - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + uses: golangci/golangci-lint-action@v6 with: - version: v1.33 + version: v1.56.2 diff --git a/.github/workflows/tags.yml b/.github/workflows/tags.yml index dc6ea02..1d56636 100644 --- a/.github/workflows/tags.yml +++ b/.github/workflows/tags.yml @@ -9,31 +9,18 @@ jobs: release: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: ~1.17 + go-version: ~1.21 - name: Create release id: goreleaser - uses: goreleaser/goreleaser-action@v3 + uses: goreleaser/goreleaser-action@v6 with: version: latest - args: release --rm-dist + args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Update install links - run: | - wget -q https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 -O jq - chmod +x ./jq - tag=$(echo '${{steps.goreleaser.outputs.metadata}}' | ./jq --raw-output '.tag') - linux_name=$(echo '${{steps.goreleaser.outputs.artifacts}}' | ./jq --raw-output '.[] | select((.goos=="linux") and (.type=="Archive")) | .name') - mac_name=$(echo '${{steps.goreleaser.outputs.artifacts}}' | ./jq --raw-output '.[] | select((.goos=="darwin") and (.type=="Archive")) | .name') - win_name=$(echo '${{steps.goreleaser.outputs.artifacts}}' | ./jq --raw-output '.[] | select((.goos=="windows") and (.type=="Archive")) | .name') - download_url_prefix="https://github.com/${{github.repository}}/releases/download/${tag}" - short_url_api_prefix="https://go.enapter.com/rest/v3/short-urls" - curl -q -H "X-Api-Key: ${{secrets.ENAPTER_SHLINK_API_KEY}}" -H "Content-Type: application/json" -X PATCH ${short_url_api_prefix}/enaptercli-linux-install -d "{\"longUrl\":\"${download_url_prefix}/${linux_name}\"}" - curl -q -H "X-Api-Key: ${{secrets.ENAPTER_SHLINK_API_KEY}}" -H "Content-Type: application/json" -X PATCH ${short_url_api_prefix}/enaptercli-macos-install -d "{\"longUrl\":\"${download_url_prefix}/${mac_name}\"}" - curl -q -H "X-Api-Key: ${{secrets.ENAPTER_SHLINK_API_KEY}}" -H "Content-Type: application/json" -X PATCH ${short_url_api_prefix}/enaptercli-windows-install -d "{\"longUrl\":\"${download_url_prefix}/${win_name}\"}" + TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} diff --git a/.golangci.yml b/.golangci.yml index 65ed0c1..e93fa58 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,8 +6,8 @@ linters: enable: - asciicheck - bodyclose - - deadcode - - depguard + # - deadcode + # - depguard - dogsled - dupl - errcheck @@ -30,7 +30,7 @@ linters: - gofumpt - goheader - goimports - - golint + # - golint - gomnd - gomodguard - goprintffuncname @@ -40,7 +40,7 @@ linters: - ineffassign # - interfacer # is prone to bad suggestions (officialy deprecated) - lll - - maligned + # - maligned - misspell - nakedret - nestif @@ -48,11 +48,12 @@ linters: - noctx - nolintlint - prealloc + - revive - rowserrcheck - - scopelint + # - scopelint - sqlclosecheck - staticcheck - - structcheck + # - structcheck - stylecheck - testpackage - tparallel @@ -60,7 +61,7 @@ linters: - unconvert - unparam - unused - - varcheck + # - varcheck - whitespace # - wrapcheck # - wsl diff --git a/.goreleaser.yml b/.goreleaser.yml index be29afc..f87ff78 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,26 +1,59 @@ ---- -project_name: enapter-cli +version: 2 -release: - github: - owner: enapter - name: enapter-cli +project_name: enapter-cli builds: - binary: enapter + main: ./cmd/enapter/ + ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.ShortCommit}} -X main.date={{.Date}} + env: + - CGO_ENABLED=0 goos: - - darwin - - windows - linux + - windows + - darwin goarch: - amd64 - env: - - CGO_ENABLED=0 - main: ./cmd/enapter/ - ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.ShortCommit}} -X main.date={{.Date}} + - arm64 + ignore: + - goos: linux + goarch: arm64 + - goos: windows + goarch: arm64 + +release: + github: + owner: enapter + name: enapter-cli + +archives: + - format: tar.gz + wrap_in_directory: true + format_overrides: + - goos: windows + format: zip + name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}' checksum: name_template: '{{ .ProjectName }}-{{ .Version }}-checksums.txt' +snapshot: + version_template: 'SNAPSHOT-{{ .Tag }}' + changelog: - skip: true + disable: true + +brews: + - repository: + owner: enapter + name: homebrew-tap + token: "{{ .Env.TAP_GITHUB_TOKEN }}" + name: enapter@3 + directory: Formula + homepage: https://github.com/Enapter/enapter-cli + description: Command-line tool for Enapter Energy Management System Toolkit + + install: | + bin.install "enapter" + test: | + assert_match "Enapter CLI #{version}", shell_output("#{bin}/enapter --version") diff --git a/README.md b/README.md index 4678657..1f93b79 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,11 @@ This tool helps Enapter customers to work with devices. It useful in the followi ###  macOS - recommended ```bash -brew tap enapter/tap && brew install enapter +brew tap enapter/tap && brew install enapter@3 ``` +If you already have installed previous version you probably need to update symblink as brew suggested you after install new version. + ### Get prebuilt binaries Choose your platform and required release on the [Releases page](https://github.com/Enapter/enapter-cli/releases). @@ -56,4 +58,4 @@ Please note that if you don't save your token, it is not possible to reveal it a In order to make life easier with command line interface, you may use [Fig - the next-generation command line](https://fig.io/). This autocompletion tool has native support for the Enapter CLI for Mac OS X and Linux. - \ No newline at end of file + diff --git a/go.mod b/go.mod index 865d5be..0766ab4 100644 --- a/go.mod +++ b/go.mod @@ -1,21 +1,17 @@ module github.com/enapter/enapter-cli -go 1.19 +go 1.21 require ( - github.com/bxcodec/faker/v3 v3.5.0 - github.com/gorilla/websocket v1.4.2 - github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a - github.com/stretchr/testify v1.6.1 - github.com/urfave/cli/v2 v2.3.0 + github.com/stretchr/testify v1.9.0 + github.com/urfave/cli/v2 v2.27.4 ) require ( - github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/russross/blackfriday/v2 v2.0.1 // indirect - github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect - golang.org/x/net v0.17.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index abef50e..b5436ad 100644 --- a/go.sum +++ b/go.sum @@ -1,30 +1,18 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/bxcodec/faker/v3 v3.5.0 h1:Rahy6dwbd6up0wbwbV7dFyQb+jmdC51kpATuUdnzfMg= -github.com/bxcodec/faker/v3 v3.5.0/go.mod h1:gF31YgnMSMKgkvl+fyEo1xuSMbEuieyqfeslGYFjneM= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a h1:KikTa6HtAK8cS1qjvUvvq4QO21QnwC+EfvB+OAuZ/ZU= -github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a/go.mod h1:AuYgA5Kyo4c7HfUmvRGs/6rGlMMV/6B1bVnB9JxJEEg= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= +github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index e941a90..97cce1c 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -1,17 +1,23 @@ package enaptercli import ( + "context" + "encoding/json" + "errors" + "fmt" "io" + "net/http" + "net/url" + "strings" "github.com/urfave/cli/v2" ) type cmdBase struct { - token string - apiHost string - graphqlURL string - websocketsURL string - writer io.Writer + token string + apiHost string + writer io.Writer + httpClient *http.Client } func (c *cmdBase) Flags() []cli.Flag { @@ -22,6 +28,7 @@ func (c *cmdBase) Flags() []cli.Flag { EnvVars: []string{"ENAPTER_API_TOKEN"}, Hidden: true, Destination: &c.token, + Category: "HTTP API Configuration:", }, &cli.StringFlag{ Name: "api-host", @@ -30,22 +37,11 @@ func (c *cmdBase) Flags() []cli.Flag { Hidden: true, Value: "https://api.enapter.com", Destination: &c.apiHost, - }, - &cli.StringFlag{ - Name: "gql-api-url", - Usage: "Override Cloud API endpoint", - EnvVars: []string{"ENAPTER_GQL_API_URL"}, - Hidden: true, - Value: "https://cli.enapter.com/graphql", - Destination: &c.graphqlURL, - }, - &cli.StringFlag{ - Name: "ws-api-url", - Usage: "Override Cloud API endpoint", - EnvVars: []string{"ENAPTER_WS_API_URL"}, - Hidden: true, - Value: "wss://cli.enapter.com/cable", - Destination: &c.websocketsURL, + Category: "HTTP API Configuration:", + Action: func(_ *cli.Context, v string) error { + c.apiHost = strings.TrimSuffix(v, "/") + return nil + }, }, } } @@ -55,12 +51,88 @@ func (c *cmdBase) Before(cliCtx *cli.Context) error { return errAPITokenMissed } c.writer = cliCtx.App.Writer + c.httpClient = http.DefaultClient return nil } func (c *cmdBase) HelpTemplate() string { - return cli.CommandHelpTemplate + `ENVIRONMENT VARIABLES: + return cli.CommandHelpTemplate + ` +ENVIRONMENT VARIABLES: ENAPTER_API_TOKEN Enapter API access token + ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) ` } + +type doHTTPRequestParams struct { + Method string + Path string + Query url.Values + Body io.Reader + RespProcessor func(*http.Response) error +} + +func (c *cmdBase) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { + req, err := http.NewRequestWithContext(ctx, p.Method, c.apiHost+"/v2"+p.Path, p.Body) + if err != nil { + return fmt.Errorf("build http request: %w", err) + } + + req.Header.Add("X-Enapter-Auth-Token", c.token) + req.URL.RawQuery = p.Query.Encode() + + resp, err := c.httpClient.Do(req) + if err != nil { + return fmt.Errorf("do http request: %w", err) + } + defer resp.Body.Close() + + if p.RespProcessor == nil { + return c.defaultRespProcessor(resp) + } + return p.RespProcessor(resp) +} + +func (c *cmdBase) defaultRespProcessor(resp *http.Response) error { + if resp.StatusCode != http.StatusOK { + return cli.Exit(parseRespErrorMessage(resp), 1) + } + + n, _ := io.Copy(c.writer, resp.Body) + if n == 0 { + _, _ = io.WriteString(c.writer, "Request finished without body\n") + } + + return nil +} + +func okRespBodyProcessor(fn func(body io.Reader) error) func(resp *http.Response) error { + return func(resp *http.Response) error { + if resp.StatusCode != http.StatusOK { + return cli.Exit(parseRespErrorMessage(resp), 1) + } + return fn(resp.Body) + } +} + +func parseRespErrorMessage(resp *http.Response) string { + var errs struct { + Errors []struct { + Message string `json:"message"` + } `json:"errors"` + } + if err := json.NewDecoder(resp.Body).Decode(&errs); err != nil { + if !errors.Is(err, io.EOF) { + return fmt.Sprintf("parse error response: %s", err) + } + } + + if len(errs.Errors) > 0 { + msg := errs.Errors[0].Message + if len(msg) > 0 { + return msg + } + } + + return fmt.Sprintf("request finished with HTTP status %q, but without error message", resp.Status) +} diff --git a/internal/app/enaptercli/cmd_blueprints.go b/internal/app/enaptercli/cmd_blueprints.go new file mode 100644 index 0000000..03bd9f6 --- /dev/null +++ b/internal/app/enaptercli/cmd_blueprints.go @@ -0,0 +1,56 @@ +package enaptercli + +import ( + "strings" + + "github.com/urfave/cli/v2" +) + +type cmdBlueprints struct { + cmdBase +} + +func buildCmdBlueprints() *cli.Command { + return &cli.Command{ + Name: "blueprints", + Usage: "Manage blueprints", + Subcommands: []*cli.Command{ + buildCmdBlueprintsUpload(), + buildCmdBlueprintsDownload(), + buildCmdBlueprintsInspect(), + }, + } +} + +func isBlueprintID(s string) bool { + const blueprintIDLen = 36 + if len(s) != blueprintIDLen { + return false + } + + isDashPos := func(i int) bool { return i == 8 || i == 13 || i == 18 || i == 23 } + for i := 0; i < blueprintIDLen; i++ { + if isDashPos(i) { + if s[i] != '-' { + return false + } + } else { + isHexDigit := (s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'f') + if !isHexDigit { + return false + } + } + } + return true +} + +func parseBlueprintName(n string) (name, tag string) { + const blueprintNameParts = 2 + nameTag := strings.SplitN(n, ":", blueprintNameParts) + name = nameTag[0] + tag = "latest" + if len(nameTag) > 1 { + tag = nameTag[1] + } + return name, tag +} diff --git a/internal/app/enaptercli/cmd_blueprints_download.go b/internal/app/enaptercli/cmd_blueprints_download.go new file mode 100644 index 0000000..54ec5ca --- /dev/null +++ b/internal/app/enaptercli/cmd_blueprints_download.go @@ -0,0 +1,96 @@ +package enaptercli + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "strings" + + "github.com/urfave/cli/v2" +) + +type cmdBlueprintsDownload struct { + cmdBlueprints + blueprintID string + outputFileName string +} + +func buildCmdBlueprintsDownload() *cli.Command { + cmd := &cmdBlueprintsDownload{} + return &cli.Command{ + Name: "download", + Usage: "Download blueprint zip from Platform", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdBlueprintsDownload) Flags() []cli.Flag { + flags := c.cmdBlueprints.Flags() + return append(flags, &cli.StringFlag{ + Name: "blueprint_id", + Aliases: []string{"b"}, + Usage: "blueprint name or ID to download", + Destination: &c.blueprintID, + Required: true, + }, &cli.StringFlag{ + Name: "output", + Aliases: []string{"o"}, + Usage: "blueprint file name to save", + Destination: &c.outputFileName, + }) +} + +func (c *cmdBlueprintsDownload) do(ctx context.Context) error { + if c.outputFileName == "" { + c.outputFileName = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(c.blueprintID, + ":", "_"), ".", "_"), "/", "_") + ".zip" + } + + if !isBlueprintID(c.blueprintID) { + blueprintName, blueprintTag := parseBlueprintName(c.blueprintID) + err := c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/blueprints/enapter/" + blueprintName + "/" + blueprintTag, + //nolint:bodyclose //body is closed in doHTTPRequest + RespProcessor: okRespBodyProcessor(func(body io.Reader) error { + var resp struct { + Blueprint struct { + ID string `json:"id"` + } `json:"blueprint"` + } + if err := json.NewDecoder(body).Decode(&resp); err != nil { + return fmt.Errorf("parse response body: %w", err) + } + c.blueprintID = resp.Blueprint.ID + return nil + }), + }) + if err != nil { + return fmt.Errorf("get blueprint info by name: %w", err) + } + } + + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/blueprints/" + c.blueprintID + "/zip", + //nolint:bodyclose //body is closed in doHTTPRequest + RespProcessor: okRespBodyProcessor(func(body io.Reader) error { + outFile, err := os.Create(c.outputFileName) + if err != nil { + return fmt.Errorf("create output file %q: %w", c.outputFileName, err) + } + if _, err := io.Copy(outFile, body); err != nil { + return fmt.Errorf("write output file %q: %w", c.outputFileName, err) + } + return nil + }), + }) +} diff --git a/internal/app/enaptercli/cmd_blueprints_inspect.go b/internal/app/enaptercli/cmd_blueprints_inspect.go new file mode 100644 index 0000000..6901281 --- /dev/null +++ b/internal/app/enaptercli/cmd_blueprints_inspect.go @@ -0,0 +1,53 @@ +package enaptercli + +import ( + "context" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdBlueprintsInpsect struct { + cmdBlueprints +} + +func buildCmdBlueprintsInspect() *cli.Command { + cmd := &cmdBlueprintsInpsect{} + return &cli.Command{ + Name: "inspect", + Usage: "Get blueprint metainfo", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Args: true, + ArgsUsage: "", + Action: func(cliCtx *cli.Context) error { + return cmd.inspect(cliCtx.Context, cliCtx.Args().Get(0)) + }, + } +} + +func (c *cmdBlueprintsInpsect) Before(cliCtx *cli.Context) error { + if err := c.cmdBlueprints.Before(cliCtx); err != nil { + return err + } + if cliCtx.Args().Get(0) == "" { + return errBlueprintIDMissed + } + return nil +} + +func (c *cmdBlueprintsInpsect) inspect(ctx context.Context, blueprintID string) error { + if isBlueprintID(blueprintID) { + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/blueprints/" + blueprintID, + }) + } + + blueprintName, blueprintTag := parseBlueprintName(blueprintID) + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/blueprints/enapter/" + blueprintName + "/" + blueprintTag, + }) +} diff --git a/internal/app/enaptercli/cmd_blueprints_upload.go b/internal/app/enaptercli/cmd_blueprints_upload.go new file mode 100644 index 0000000..a9d1ce2 --- /dev/null +++ b/internal/app/enaptercli/cmd_blueprints_upload.go @@ -0,0 +1,68 @@ +package enaptercli + +import ( + "bytes" + "context" + "fmt" + "net/http" + "os" + + "github.com/urfave/cli/v2" +) + +type cmdBlueprintsUpload struct { + cmdBlueprints +} + +func buildCmdBlueprintsUpload() *cli.Command { + cmd := &cmdBlueprintsUpload{} + return &cli.Command{ + Name: "upload", + Usage: "Upload blueprint directory into Platform", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Args: true, + ArgsUsage: "", + Action: func(cliCtx *cli.Context) error { + return cmd.upload(cliCtx.Context, cliCtx.Args().Get(0)) + }, + } +} + +func (c *cmdBlueprintsUpload) Before(cliCtx *cli.Context) error { + if err := c.cmdBlueprints.Before(cliCtx); err != nil { + return err + } + + if cliCtx.Args().Get(0) == "" { + return errBlueprintPathMissed + } + return nil +} + +func (c *cmdBlueprintsUpload) upload(ctx context.Context, blueprintPath string) error { + fi, err := os.Stat(blueprintPath) + if err != nil { + return fmt.Errorf("check blueprint path: %w", err) + } + + var data []byte + if fi.IsDir() { + data, err = zipDir(blueprintPath) + if err != nil { + return fmt.Errorf("zip blueprint directory: %w", err) + } + } else { + data, err = os.ReadFile(blueprintPath) + if err != nil { + return fmt.Errorf("read blueprint zip file: %w", err) + } + } + + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/blueprints/upload", + Body: bytes.NewReader(data), + }) +} diff --git a/internal/app/enaptercli/cmd_devices.go b/internal/app/enaptercli/cmd_devices.go index 7782b4a..56d88eb 100644 --- a/internal/app/enaptercli/cmd_devices.go +++ b/internal/app/enaptercli/cmd_devices.go @@ -1,32 +1,61 @@ package enaptercli -import "github.com/urfave/cli/v2" +import ( + "context" + "encoding/json" + "fmt" + "io" + + "github.com/urfave/cli/v2" +) type cmdDevices struct { cmdBase - hardwareID string + deviceID string } func buildCmdDevices() *cli.Command { return &cli.Command{ Name: "devices", - Usage: "Device information and management commands.", + Usage: "Manage devices", Subcommands: []*cli.Command{ - buildCmdDevicesUpload(), + buildCmdDevicesAssignBlueprint(), buildCmdDevicesLogs(), - buildCmdDevicesUploadLogs(), - buildCmdDevicesExecute(), + buildCmdDevicesLogsf(), }, } } func (c *cmdDevices) Flags() []cli.Flag { flags := c.cmdBase.Flags() - flags = append(flags, &cli.StringFlag{ - Name: "hardware-id", - Usage: "Hardware ID of the device; can be obtained in cloud.enapter.com", + return append(flags, &cli.StringFlag{ + Name: "device_id", + Aliases: []string{"d"}, + Usage: "device ID", + Destination: &c.deviceID, Required: true, - Destination: &c.hardwareID, }) - return flags +} + +func (c *cmdDevices) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { + p.Path = "/devices/" + c.deviceID + return c.cmdBase.doHTTPRequest(ctx, p) +} + +func (c *cmdDevices) parseAndDumpDeviceLogs(body io.Reader) (int, error) { + var resp struct { + Logs []struct { + ReceivedAt string `json:"received_at"` + Timestamp string `json:"timestamp"` + Severity string `json:"severity"` + Message string `json:"message"` + } `json:"logs"` + } + if err := json.NewDecoder(body).Decode(&resp); err != nil { + return 0, fmt.Errorf("parse response body: %w", err) + } + for _, l := range resp.Logs { + fmt.Fprintf(c.writer, "%s [%s] %s\n", l.ReceivedAt, l.Severity, l.Message) + } + return len(resp.Logs), nil } diff --git a/internal/app/enaptercli/cmd_devices_assign_blueprint.go b/internal/app/enaptercli/cmd_devices_assign_blueprint.go new file mode 100644 index 0000000..39bc2c9 --- /dev/null +++ b/internal/app/enaptercli/cmd_devices_assign_blueprint.go @@ -0,0 +1,55 @@ +package enaptercli + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdDevicesAssignBlueprint struct { + cmdDevices + blueprintID string +} + +func buildCmdDevicesAssignBlueprint() *cli.Command { + cmd := &cmdDevicesAssignBlueprint{} + return &cli.Command{ + Name: "assign_blueprint", + Usage: "Assign blueprint to device", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdDevicesAssignBlueprint) Flags() []cli.Flag { + flags := c.cmdDevices.Flags() + return append(flags, &cli.StringFlag{ + Name: "blueprint_id", + Aliases: []string{"b"}, + Usage: "blueprint ID to assign", + Destination: &c.blueprintID, + Required: true, + }) +} + +func (c *cmdDevicesAssignBlueprint) do(ctx context.Context) error { + body, err := json.Marshal(map[string]interface{}{ + "blueprint_id": c.blueprintID, + }) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/assign_blueprint", + Body: bytes.NewReader(body), + }) +} diff --git a/internal/app/enaptercli/cmd_devices_execute.go b/internal/app/enaptercli/cmd_devices_execute.go deleted file mode 100644 index 6f15714..0000000 --- a/internal/app/enaptercli/cmd_devices_execute.go +++ /dev/null @@ -1,119 +0,0 @@ -package enaptercli - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - - "github.com/urfave/cli/v2" - - "github.com/enapter/enapter-cli/internal/publichttp" -) - -type cmdDevicesExecute struct { - cmdDevices - commandName string - arguments string - showProgress bool -} - -func buildCmdDevicesExecute() *cli.Command { - cmd := &cmdDevicesExecute{} - - return &cli.Command{ - Name: "execute", - Usage: "Execute command on device", - CustomHelpTemplate: cmd.HelpTemplate(), - Flags: cmd.Flags(), - Before: cmd.Before, - Action: func(cliCtx *cli.Context) error { - return cmd.execute(cliCtx.Context) - }, - } -} - -func (c *cmdDevicesExecute) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() - flags = append(flags, - &cli.StringFlag{ - Name: "command", - Usage: "Command name", - Required: true, - Destination: &c.commandName, - }, - &cli.StringFlag{ - Name: "arguments", - Usage: "Command arguments as JSON object", - Destination: &c.arguments, - }, - &cli.BoolFlag{ - Name: "show-progress", - Usage: "Enable in-progress responses streaming", - Destination: &c.showProgress, - }, - ) - return flags -} - -func (c *cmdDevicesExecute) execute(ctx context.Context) error { - transport := publichttp.NewAuthTokenTransport(http.DefaultTransport, c.token) - client, err := publichttp.NewClientWithURL(&http.Client{Transport: transport}, c.apiHost) - if err != nil { - return fmt.Errorf("create http client: %w", err) - } - - var arguments map[string]interface{} - if c.arguments != "" { - if err := json.Unmarshal([]byte(c.arguments), &arguments); err != nil { - return fmt.Errorf("parse arguments: %w", err) - } - } - - query := publichttp.CommandQuery{ - HardwareID: c.hardwareID, - CommandName: c.commandName, - Arguments: arguments, - } - - if c.showProgress { - return c.executeWithProgress(ctx, client, query) - } - - response, err := client.Commands.Execute(ctx, query) - if err != nil { - return err - } - return c.print(response) -} - -func (c *cmdDevicesExecute) executeWithProgress( - ctx context.Context, client *publichttp.Client, - query publichttp.CommandQuery, -) error { - progressCh, err := client.Commands.ExecuteWithProgress(ctx, query) - if err != nil { - return err - } - - for progress := range progressCh { - if progress.Error != nil { - return progress.Error - } - err := c.print(progress.CommandResponse) - if err != nil { - return err - } - } - - return nil -} - -func (c *cmdDevicesExecute) print(r publichttp.CommandResponse) error { - s, err := json.Marshal(r) - if err != nil { - return fmt.Errorf("format response: %w", err) - } - fmt.Fprintln(c.writer, string(s)) - return nil -} diff --git a/internal/app/enaptercli/cmd_devices_execute_test.go b/internal/app/enaptercli/cmd_devices_execute_test.go deleted file mode 100644 index 3b69ecc..0000000 --- a/internal/app/enaptercli/cmd_devices_execute_test.go +++ /dev/null @@ -1,101 +0,0 @@ -package enaptercli_test - -import ( - "bytes" - "io" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "testing" - - "github.com/bxcodec/faker/v3" - "github.com/stretchr/testify/require" -) - -func TestDeviceExecute(t *testing.T) { - t.Run("simple", func(t *testing.T) { - basePath := "testdata/device_execute/simple" - showProgress := false - testDeviceExecute(t, basePath, showProgress, http.StatusOK) - }) - - t.Run("progress", func(t *testing.T) { - basePath := "testdata/device_execute/progress" - showProgress := true - testDeviceExecute(t, basePath, showProgress, http.StatusOK) - }) - - t.Run("error", func(t *testing.T) { - basePath := "testdata/device_execute/error" - showProgress := false - testDeviceExecute(t, basePath, showProgress, http.StatusForbidden) - }) -} - -func testDeviceExecute( - t *testing.T, basePath string, showProgress bool, statusCode int, -) { - resp := readFileLines(t, filepath.Join(basePath, "responses")) - server := startExecuteTestServer(showProgress, statusCode, resp) - defer server.Close() - - args := []string{"enapter", "devices", "execute"} - args = append(args, - "--token", faker.Word(), - "--hardware-id", faker.Word(), - "--command", faker.Word(), - "--api-host", server.URL) - if showProgress { - args = append(args, "--show-progress") - } - - checkExecuteTestAppOutput(t, basePath, args) -} - -func readFileLines(t *testing.T, path string) [][]byte { - f, err := os.ReadFile(path) - require.NoError(t, err) - return bytes.Split(f, []byte{'\n'}) -} - -func startExecuteTestServer( - showProgress bool, statusCode int, responses [][]byte, -) *httptest.Server { - handler := func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(statusCode) - - for _, r := range responses { - _, _ = w.Write(append(r, '\n')) - if showProgress { - w.(http.Flusher).Flush() - } - } - } - return httptest.NewServer(http.HandlerFunc(handler)) -} - -func checkExecuteTestAppOutput(t *testing.T, basePath string, args []string) { - app := startTestApp(args...) - defer app.Stop() - - appErr := app.Wait() - - actual, err := io.ReadAll(app.Stdout()) - require.NoError(t, err) - - if appErr != nil { - actual = append(actual, []byte("app exit with error: "+appErr.Error()+"\n")...) - } - - expectedFileName := filepath.Join(basePath, "output") - if update { - err := os.WriteFile(expectedFileName, actual, 0o600) - require.NoError(t, err) - } - - expected, err := os.ReadFile(expectedFileName) - require.NoError(t, err) - - require.Equal(t, string(expected), string(actual)) -} diff --git a/internal/app/enaptercli/cmd_devices_logs.go b/internal/app/enaptercli/cmd_devices_logs.go index 1436c40..9e5a6d2 100644 --- a/internal/app/enaptercli/cmd_devices_logs.go +++ b/internal/app/enaptercli/cmd_devices_logs.go @@ -3,40 +3,124 @@ package enaptercli import ( "context" "fmt" + "io" + "net/http" + "net/url" + "strconv" + "time" "github.com/urfave/cli/v2" - - "github.com/enapter/enapter-cli/internal/cloudapi" ) type cmdDevicesLogs struct { cmdDevices + from cli.Timestamp + to cli.Timestamp + offset int + limit int + severity string + order string + showFilter string } func buildCmdDevicesLogs() *cli.Command { cmd := &cmdDevicesLogs{} - return &cli.Command{ Name: "logs", - Usage: "Stream logs from a device", + Usage: "Show device logs", CustomHelpTemplate: cmd.HelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { - return cmd.run(cliCtx.Context, cliCtx.App.Version) + return cmd.do(cliCtx.Context) }, } } -func (c *cmdDevicesLogs) run(ctx context.Context, version string) error { - writer, err := cloudapi.NewDeviceLogsWriter(c.websocketsURL, c.token, - version, c.hardwareID, c.writeLog) - if err != nil { - return fmt.Errorf("create writer: %w", err) - } - return writer.Run(ctx) +func (c *cmdDevicesLogs) Flags() []cli.Flag { + flags := c.cmdDevices.Flags() + return append(flags, &cli.TimestampFlag{ + Name: "from", + Aliases: []string{"f"}, + Usage: "from timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z)", + Destination: &c.from, + Layout: time.RFC3339, + }, &cli.TimestampFlag{ + Name: "to", + Aliases: []string{"t"}, + Usage: "to timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z)", + Destination: &c.to, + Layout: time.RFC3339, + }, &cli.IntFlag{ + Name: "limit", + Aliases: []string{"l"}, + Usage: "maximum number of logs to retrieve", + Destination: &c.limit, + }, &cli.IntFlag{ + Name: "offset", + Aliases: []string{"o"}, + Usage: "number of logs to skip on retrieve", + Destination: &c.offset, + }, &cli.StringFlag{ + Name: "severity", + Aliases: []string{"s"}, + Usage: "filter logs by severity", + Destination: &c.severity, + }, &cli.StringFlag{ + Name: "order", + Usage: "order logs by criteria (received_at_asc[default], received_at_desc)", + Destination: &c.order, + Action: func(_ *cli.Context, v string) error { + if v != "received_at_asc" && v != "received_at_desc" { + return fmt.Errorf("%w: should be one of [received_at_asc, received_at_desc]", errUnsupportedFlagValue) + } + return nil + }, + }, &cli.StringFlag{ + Name: "show", + Usage: "filter logs by criteria (all[default], persist_only, temporary_only)", + Destination: &c.showFilter, + Action: func(_ *cli.Context, v string) error { + if v != "all" && v != "persist_only" && v != "temporary_only" { + return fmt.Errorf("%w: should be one of [all, persist_only, temporary_only]", errUnsupportedFlagValue) + } + return nil + }, + }) } -func (c *cmdDevicesLogs) writeLog(topic, msg string) { - fmt.Fprintf(c.writer, "[%s] %s\n", topic, msg) +func (c *cmdDevicesLogs) do(ctx context.Context) error { + query := url.Values{} + if c.from.Value() != nil { + query.Add("received_at_from", c.from.Value().Format(time.RFC3339)) + } + if c.to.Value() != nil { + query.Add("received_at_to", c.to.Value().Format(time.RFC3339)) + } + if c.offset > 0 { + query.Add("offset", strconv.Itoa(c.offset)) + } + if c.limit > 0 { + query.Add("limit", strconv.Itoa(c.limit)) + } + if c.severity != "" { + query.Add("severity", c.severity) + } + if c.order != "" { + query.Add("order", c.order) + } + if c.showFilter != "" { + query.Add("show", c.showFilter) + } + + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/logs", + Query: query, + //nolint:bodyclose //body is closed in doHTTPRequest + RespProcessor: okRespBodyProcessor(func(body io.Reader) error { + _, err := c.parseAndDumpDeviceLogs(body) + return err + }), + }) } diff --git a/internal/app/enaptercli/cmd_devices_logs_test.go b/internal/app/enaptercli/cmd_devices_logs_test.go deleted file mode 100644 index 250c353..0000000 --- a/internal/app/enaptercli/cmd_devices_logs_test.go +++ /dev/null @@ -1,41 +0,0 @@ -//nolint:dupl // not a duplicate of `rules logs` command tests -package enaptercli_test - -import ( - "strings" - "testing" -) - -func TestDeviceLogs(t *testing.T) { - t.Run("simple", func(t *testing.T) { - inputFileName := "testdata/device_logs/simple/input" - untilLinePrefix := "[telemetry]" - expectedFileName := "testdata/device_logs/simple/output" - testDeviceLogs(t, inputFileName, untilLinePrefix, expectedFileName) - }) - - t.Run("invalid token", func(t *testing.T) { - inputFileName := "testdata/device_logs/disconnect/invalid_token/input" - untilLinePrefix := "[connection]" - expectedFileName := "testdata/device_logs/disconnect/invalid_token/output" - testDeviceLogs(t, inputFileName, untilLinePrefix, expectedFileName) - }) - - t.Run("device not found", func(t *testing.T) { - inputFileName := "testdata/device_logs/disconnect/device_not_found/input" - untilLinePrefix := "[connection] disconnected" - expectedFileName := "testdata/device_logs/disconnect/device_not_found/output" - testDeviceLogs(t, inputFileName, untilLinePrefix, expectedFileName) - }) -} - -func testDeviceLogs(t *testing.T, inputFileName, untilLinePrefix, expectedFileName string) { - const hardwareID = "SIM-WTM" - - identifier := map[string]string{"channel": "DeviceChannel", "hardware_id": hardwareID} - - command := strings.Split("enapter devices logs", " ") - command = append(command, "--hardware-id", hardwareID) - - testLogsCommand(t, inputFileName, untilLinePrefix, expectedFileName, identifier, command) -} diff --git a/internal/app/enaptercli/cmd_devices_logsf.go b/internal/app/enaptercli/cmd_devices_logsf.go new file mode 100644 index 0000000..f9d9d02 --- /dev/null +++ b/internal/app/enaptercli/cmd_devices_logsf.go @@ -0,0 +1,71 @@ +package enaptercli + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/urfave/cli/v2" +) + +type cmdDevicesLogsf struct { + cmdDevices +} + +func buildCmdDevicesLogsf() *cli.Command { + cmd := &cmdDevicesLogsf{} + return &cli.Command{ + Name: "logsf", + Usage: "Follow device logs", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdDevicesLogsf) do(ctx context.Context) error { + const singleRequestLimit = 10 + + query := url.Values{} + query.Add("received_at_from", time.Now().Add(-time.Hour).UTC().Format(time.RFC3339)) + query.Add("order", "received_at_asc") + query.Add("limit", strconv.Itoa(singleRequestLimit)) + + offset := 0 + + for { + retryNow := false + err := c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/logs", + Query: query, + //nolint:bodyclose //body is closed in doHTTPRequest + RespProcessor: okRespBodyProcessor(func(body io.Reader) error { + n, err := c.parseAndDumpDeviceLogs(body) + retryNow = n == singleRequestLimit + offset += n + query.Set("offset", strconv.Itoa(offset)) + return err + }), + }) + if err != nil { + if errors.Is(err, context.Canceled) { + return nil + } + fmt.Fprintf(c.writer, "Failed to retrieve logs: %s\n", err) + continue + } + + if !retryNow { + time.Sleep(time.Second) + } + } +} diff --git a/internal/app/enaptercli/cmd_devices_upload.go b/internal/app/enaptercli/cmd_devices_upload.go deleted file mode 100644 index d8c485e..0000000 --- a/internal/app/enaptercli/cmd_devices_upload.go +++ /dev/null @@ -1,156 +0,0 @@ -package enaptercli - -import ( - "bytes" - "context" - "encoding/base64" - "fmt" - "net/http" - "os" - "path/filepath" - "time" - - "github.com/urfave/cli/v2" - - "github.com/enapter/enapter-cli/internal/cloudapi" -) - -const deviceUploadDefaultTimeout = 30 * time.Second - -type cmdDevicesUpload struct { - cmdDevices - blueprintDir string - timeout time.Duration -} - -func buildCmdDevicesUpload() *cli.Command { - cmd := &cmdDevicesUpload{} - - return &cli.Command{ - Name: "upload", - Usage: "Upload blueprint to a device", - Description: "Blueprint combines device capabilities declaration and Lua firmware for Enapter UCM. " + - "The command updates device blueprint and uploads the firmware to the UCM. Learn more " + - "about Enapter Blueprints at https://handbook.enapter.com/blueprints.", - CustomHelpTemplate: cmd.HelpTemplate(), - Flags: cmd.Flags(), - Before: cmd.Before, - Action: func(cliCtx *cli.Context) error { - return cmd.upload(cliCtx.Context, cliCtx.App.Version) - }, - } -} - -func (c *cmdDevicesUpload) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() - flags = append(flags, - &cli.DurationFlag{ - Name: "timeout", - Usage: "Time to wait for blueprint uploading", - Destination: &c.timeout, - Value: deviceUploadDefaultTimeout, - }, - &cli.StringFlag{ - Name: "blueprint-dir", - Usage: "Directory which contains blueprint file", - Required: true, - Destination: &c.blueprintDir, - }, - ) - return flags -} - -func (c *cmdDevicesUpload) upload(ctx context.Context, version string) error { - if c.timeout != 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, c.timeout) - defer cancel() - } - - files, err := c.blueprintFilesList() - if err != nil { - return err - } - - fmt.Fprintln(c.writer, "Blueprint files to be uploaded:") - for _, name := range files { - fmt.Fprintln(c.writer, "*", name) - } - - zipBytes, err := c.blueprintZip() - if err != nil { - return err - } - - onceWriter := &onceWriter{w: c.writer} - transport := cloudapi.NewCredentialsTransport(http.DefaultTransport, c.token, version) - transport = cloudapi.NewCLIMessageWriterTransport(transport, onceWriter) - client := cloudapi.NewClientWithURL(&http.Client{Transport: transport}, c.graphqlURL) - - uploadData, uploadErrors, err := client.UploadBlueprint(ctx, c.hardwareID, zipBytes) - if err != nil { - return fmt.Errorf("do update: %w", err) - } - - if len(uploadErrors) != 0 { - for _, e := range uploadErrors { - fmt.Fprintln(c.writer, "[ERROR]", e.Message) - } - return errFinishedWithError - } - - fmt.Fprintln(c.writer, "upload started with operation id", uploadData.OperationID) - - err = client.WriteOperationLogs(ctx, c.hardwareID, uploadData.OperationID, c.writeLog) - if err != nil { - return fmt.Errorf("receive operation logs: %w", err) - } - - fmt.Fprintln(c.writer, "Done!") - return nil -} - -func (c *cmdDevicesUpload) blueprintFilesList() ([]string, error) { - var files []string - - err := filepath.Walk(c.blueprintDir, - func(name string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if !info.IsDir() { - files = append(files, name) - } - return nil - }) - if err != nil { - return nil, err - } - - return files, nil -} - -func (c *cmdDevicesUpload) blueprintZip() ([]byte, error) { - bpBytes, err := zipDir(c.blueprintDir) - if err != nil { - return nil, fmt.Errorf("failed to zip blueprint dir %q: %w", c.blueprintDir, err) - } - - zipBuf := &bytes.Buffer{} - zipBuf.WriteString("data:application/gzip;base64,") - enc := base64.NewEncoder(base64.StdEncoding, zipBuf) - _, err = enc.Write(bpBytes) - if err != nil { - return nil, fmt.Errorf("failed to encode blueprint as base64: %w", err) - } - - if err := enc.Close(); err != nil { - return nil, fmt.Errorf("failed to encode blueprint as base64: %w", err) - } - - return zipBuf.Bytes(), nil -} - -func (c *cmdDevicesUpload) writeLog(operationID string, l cloudapi.OperationLog) { - fmt.Fprintf(c.writer, "[#%s] %s [%s] %s\n", operationID, l.CreatedAt, l.Severity, l.Payload) -} diff --git a/internal/app/enaptercli/cmd_devices_upload_logs.go b/internal/app/enaptercli/cmd_devices_upload_logs.go deleted file mode 100644 index 7051808..0000000 --- a/internal/app/enaptercli/cmd_devices_upload_logs.go +++ /dev/null @@ -1,74 +0,0 @@ -package enaptercli - -import ( - "context" - "fmt" - "net/http" - "time" - - "github.com/urfave/cli/v2" - - "github.com/enapter/enapter-cli/internal/cloudapi" -) - -type cmdDevicesUploadLogs struct { - cmdDevices - operationID string - timeout time.Duration -} - -func buildCmdDevicesUploadLogs() *cli.Command { - cmd := &cmdDevicesUploadLogs{} - - return &cli.Command{ - Name: "upload-logs", - Usage: "Show blueprint uploading logs", - CustomHelpTemplate: cmd.HelpTemplate(), - Flags: cmd.Flags(), - Before: cmd.Before, - Action: func(cliCtx *cli.Context) error { - return cmd.run(cliCtx.Context, cliCtx.App.Version) - }, - } -} - -func (c *cmdDevicesUploadLogs) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() - flags = append(flags, - &cli.DurationFlag{ - Name: "timeout", - Usage: "Time to wait for blueprint uploading", - Destination: &c.timeout, - Value: deviceUploadDefaultTimeout, - }, - &cli.StringFlag{ - Name: "operation-id", - Usage: "Uploading operation ID (optional)", - Destination: &c.operationID, - }, - ) - return flags -} - -func (c *cmdDevicesUploadLogs) run(ctx context.Context, version string) error { - if c.timeout != 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, c.timeout) - defer cancel() - } - - transport := cloudapi.NewCredentialsTransport(http.DefaultTransport, c.token, version) - transport = cloudapi.NewCLIMessageWriterTransport(transport, &onceWriter{w: c.writer}) - client := cloudapi.NewClientWithURL(&http.Client{Transport: transport}, c.graphqlURL) - - if c.operationID != "" { - return client.WriteOperationLogs(ctx, c.hardwareID, c.operationID, c.writeLog) - } - - const lastOperationsNumber = 2 - return client.WriteLastOperationsLogs(ctx, c.hardwareID, lastOperationsNumber, c.writeLog) -} - -func (c *cmdDevicesUploadLogs) writeLog(operationID string, l cloudapi.OperationLog) { - fmt.Fprintf(c.writer, "[#%s] %s [%s] %s\n", operationID, l.CreatedAt, l.Severity, l.Payload) -} diff --git a/internal/app/enaptercli/cmd_devices_upload_logs_test.go b/internal/app/enaptercli/cmd_devices_upload_logs_test.go deleted file mode 100644 index d574df4..0000000 --- a/internal/app/enaptercli/cmd_devices_upload_logs_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package enaptercli_test - -import ( - "encoding/json" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/bxcodec/faker/v3" - "github.com/stretchr/testify/require" -) - -func TestDeviceUploadLogs(t *testing.T) { - errorsDir := "testdata/device_upload_logs" - dirs, err := os.ReadDir(errorsDir) - require.NoError(t, err) - - for _, dir := range dirs { - if !dir.IsDir() { - continue - } - - dir := dir - t.Run(dir.Name(), func(t *testing.T) { - testDeviceUploadLogs(t, filepath.Join(errorsDir, dir.Name())) - }) - } -} - -type devicesUploadLogsTestSettings struct { - OperationID string `json:"operation_id"` - HardwareID string `json:"hardware_id"` - CliMessage string `json:"cli_message"` - Token string `json:"-"` -} - -func (s *devicesUploadLogsTestSettings) Fill(t *testing.T, dir string) { - settingsBytes, err := os.ReadFile(filepath.Join(dir, "settings.json")) - require.NoError(t, err) - require.NoError(t, json.Unmarshal(settingsBytes, s)) - s.Token = faker.Word() -} - -func testDeviceUploadLogs(t *testing.T, dir string) { - var settings devicesUploadLogsTestSettings - settings.Fill(t, dir) - - reqs := byteSliceSliceFromFile(t, filepath.Join(dir, "requests")) - resps := byteSliceSliceFromFile(t, filepath.Join(dir, "responses")) - - srv := startTestServer(reqs, resps, settings.CliMessage) - defer srv.Close() - - args := strings.Split("enapter devices upload-logs", " ") - args = append(args, - "--token", settings.Token, - "--hardware-id", settings.HardwareID, - "--gql-api-url", srv.URL) - if settings.OperationID != "" { - args = append(args, "--operation-id", settings.OperationID) - } - - checkTestAppOutput(t, dir, args, reqs) -} diff --git a/internal/app/enaptercli/cmd_devices_upload_test.go b/internal/app/enaptercli/cmd_devices_upload_test.go deleted file mode 100644 index 4cfe889..0000000 --- a/internal/app/enaptercli/cmd_devices_upload_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package enaptercli_test - -import ( - "encoding/json" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/bxcodec/faker/v3" - "github.com/stretchr/testify/require" -) - -const blueprintDir = "testdata/device_upload/simple/blueprint" - -func TestDeviceUpload(t *testing.T) { - testdataDir := "testdata/device_upload" - dirs, err := os.ReadDir(testdataDir) - require.NoError(t, err) - - for _, dir := range dirs { - if !dir.IsDir() { - continue - } - - dir := dir - t.Run(dir.Name(), func(t *testing.T) { - testDeviceUpload(t, filepath.Join(testdataDir, dir.Name()), blueprintDir) - }) - } -} - -func TestDeviceUploadBlueprintDirWithDot(t *testing.T) { - testDeviceUpload(t, "testdata/device_upload/simple", "./"+blueprintDir) -} - -func TestDeviceUploadWrongBlueprintDir(t *testing.T) { - args := strings.Split("enapter devices upload --token token --hardware-id hardwareID "+ - "--gql-api-url apiURL --blueprint-dir wrong", " ") - app := startTestApp(args...) - defer app.Stop() - - appErr := app.Wait() - require.EqualError(t, appErr, `lstat wrong: no such file or directory`) -} - -type devicesUploadTestSettings struct { - HardwareID string `json:"hardware_id"` - CliMessage string `json:"cli_message"` - Token string `json:"-"` -} - -func (s *devicesUploadTestSettings) Fill(t *testing.T, dir string) { - settingsBytes, err := os.ReadFile(filepath.Join(dir, "settings.json")) - require.NoError(t, err) - require.NoError(t, json.Unmarshal(settingsBytes, s)) - s.Token = faker.Word() -} - -func testDeviceUpload(t *testing.T, dir, blueprintDir string) { - var settings devicesUploadTestSettings - settings.Fill(t, dir) - - reqs := byteSliceSliceFromFile(t, filepath.Join(dir, "requests")) - resps := byteSliceSliceFromFile(t, filepath.Join(dir, "responses")) - - srv := startTestServer(reqs, resps, settings.CliMessage) - defer srv.Close() - - args := strings.Split("enapter devices upload", " ") - args = append(args, - "--token", settings.Token, - "--hardware-id", settings.HardwareID, - "--blueprint-dir", blueprintDir, - "--gql-api-url", srv.URL) - - checkTestAppOutput(t, dir, args, reqs) -} diff --git a/internal/app/enaptercli/cmd_logs_test.go b/internal/app/enaptercli/cmd_logs_test.go deleted file mode 100644 index 0db80b7..0000000 --- a/internal/app/enaptercli/cmd_logs_test.go +++ /dev/null @@ -1,196 +0,0 @@ -package enaptercli_test - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "net/http/httptest" - "net/url" - "os" - "reflect" - "strings" - "testing" - "time" - - "github.com/bxcodec/faker/v3" - "github.com/gorilla/websocket" - "github.com/stretchr/testify/require" -) - -func testLogsCommand( - t *testing.T, inputFileName, untilLinePrefix, expectedFileName string, - identifier map[string]string, args []string, -) { - token := faker.Word() - messagesBytes, err := os.ReadFile(inputFileName) - require.NoError(t, err) - messages := bytes.Split(messagesBytes, []byte{'\n'}) - - handleErrCh := make(chan string) - wsPath, srv := startTestLogsServer(t, token, identifier, messages, handleErrCh) - defer srv.Close() - - args = append(args, "--token", token, "--ws-api-url", wsPath) - app := startTestApp(args...) - defer app.Stop() - - actual := readOutputUntilLineOrError(t, app.Stdout(), untilLinePrefix, handleErrCh) - if update { - err := os.WriteFile(expectedFileName, []byte(actual), 0o600) - require.NoError(t, err) - } - - expected, err := os.ReadFile(expectedFileName) - require.NoError(t, err) - require.Equal(t, string(expected), actual) - - app.Stop() - appErr := app.Wait() - require.NoError(t, appErr) - - restOutput, err := io.ReadAll(app.Stdout()) - require.NoError(t, err) - require.Empty(t, string(restOutput)) -} - -func startTestLogsServer( - t *testing.T, token string, identifier map[string]string, messages [][]byte, - handleErrCh chan<- string, -) (string, *httptest.Server) { - t.Helper() - - handler := buildTestLogsHandler(token, identifier, messages, handleErrCh) - srv := httptest.NewServer(handler) - - u, err := url.Parse(srv.URL) - require.NoError(t, err) - u.Scheme = "ws" - - return u.String(), srv -} - -func readOutputUntilLineOrError( - t *testing.T, r *lineBuffer, prefix string, wsHandleErrCh <-chan string, -) string { - t.Helper() - - readStr, readErr := startBackgroundReadUntilLine(r, prefix) - - timer := time.NewTimer(5 * time.Second) - select { - case <-timer.C: - require.Fail(t, "read output timed out") - case errStr := <-wsHandleErrCh: - require.Failf(t, "ws handler finished with error", errStr) - case err := <-readErr: - require.Failf(t, "read finished with error", err.Error()) - case s := <-readStr: - return s - } - - return "" -} - -func startBackgroundReadUntilLine(r *lineBuffer, prefix string) (<-chan string, <-chan error) { - readStr := make(chan string, 1) - readErr := make(chan error, 1) - - go func() { - buf := strings.Builder{} - - for { - s, err := r.ReadLine() - if err != nil { - readErr <- err - return - } - - buf.WriteString(s) - - if strings.HasPrefix(s, prefix) { - readStr <- buf.String() - return - } - } - }() - - return readStr, readErr -} - -//nolint:funlen // because contains a lot of simple logged checks. -func buildTestLogsHandler( - token string, identifier map[string]string, messages [][]byte, handleErrCh chan<- string, -) http.Handler { - f := func(w http.ResponseWriter, r *http.Request) { - reqToken := r.URL.Query().Get("token") - if reqToken != token { - w.WriteHeader(http.StatusBadRequest) - handleErrCh <- fmt.Sprintf("unexpected token %q, should be %q", reqToken, token) - return - } - - upgrader := websocket.Upgrader{} - c, err := upgrader.Upgrade(w, r, nil) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - handleErrCh <- fmt.Sprintf("failed to upgrade: %s", err) - return - } - defer c.Close() - - msgType, msgBytes, err := c.ReadMessage() - if err != nil { - handleErrCh <- fmt.Sprintf("failed to read subscribe message: %s", err) - return - } - - if msgType != websocket.TextMessage { - handleErrCh <- fmt.Sprintf("subscribe message should be text type [%d], but [%d]", - websocket.TextMessage, msgType) - return - } - - subMsg := struct { - Command string `json:"command"` - Identifier string `json:"identifier"` - }{} - if err := json.Unmarshal(msgBytes, &subMsg); err != nil { - handleErrCh <- fmt.Sprintf("failed to unmarshall subsribe message %q: %s", string(msgBytes), err.Error()) - return - } - - if subMsg.Command != "subscribe" { - handleErrCh <- fmt.Sprintf("this is not subscribe message, but %q", subMsg.Command) - return - } - - var reqIdentifier map[string]string - if err := json.Unmarshal([]byte(subMsg.Identifier), &reqIdentifier); err != nil { - handleErrCh <- fmt.Sprintf("failed to unmarshall subsribe message identifier %q: %s", - subMsg.Identifier, err.Error()) - return - } - - if !reflect.DeepEqual(identifier, reqIdentifier) { - handleErrCh <- fmt.Sprintf("subsribe message identifier shoud be equal to %q, but %q", - identifier, reqIdentifier) - return - } - - for _, m := range messages { - if len(m) == 0 { - continue - } - if err := c.WriteMessage(websocket.TextMessage, m); err != nil { - handleErrCh <- fmt.Sprintf("failed to write message: %s", err.Error()) - return - } - } - - <-r.Context().Done() - } - - return http.HandlerFunc(f) -} diff --git a/internal/app/enaptercli/cmd_provisioning.go b/internal/app/enaptercli/cmd_provisioning.go new file mode 100644 index 0000000..cf019b6 --- /dev/null +++ b/internal/app/enaptercli/cmd_provisioning.go @@ -0,0 +1,20 @@ +package enaptercli + +import ( + "github.com/urfave/cli/v2" +) + +type cmdProvisioning struct { + cmdBase +} + +func buildCmdProvisioning() *cli.Command { + return &cli.Command{ + Name: "provisioning", + Usage: "Create devices of different types", + Subcommands: []*cli.Command{ + buildCmdProvisioningStandalone(), + buildCmdProvisioningLua(), + }, + } +} diff --git a/internal/app/enaptercli/cmd_provisioning_lua_device.go b/internal/app/enaptercli/cmd_provisioning_lua_device.go new file mode 100644 index 0000000..3421240 --- /dev/null +++ b/internal/app/enaptercli/cmd_provisioning_lua_device.go @@ -0,0 +1,71 @@ +package enaptercli + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdProvisioningLua struct { + cmdProvisioning + deviceName string + runtimeID string + blueprintID string +} + +func buildCmdProvisioningLua() *cli.Command { + cmd := &cmdProvisioningLua{} + return &cli.Command{ + Name: "lua_device", + Usage: "Create a new Lua device", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdProvisioningLua) Flags() []cli.Flag { + flags := c.cmdProvisioning.Flags() + return append(flags, &cli.StringFlag{ + Name: "runtime_id", + Aliases: []string{"r"}, + Usage: "runtime UCM device ID where to run a new Lua device", + Destination: &c.runtimeID, + Required: true, + }, &cli.StringFlag{ + Name: "device_name", + Aliases: []string{"n"}, + Usage: "name of a new Lua device", + Destination: &c.deviceName, + Required: true, + }, &cli.StringFlag{ + Name: "blueprint_id", + Aliases: []string{"b"}, + Usage: "blueprint ID of a new Lua device", + Destination: &c.blueprintID, + Required: true, + }) +} + +func (c *cmdProvisioningLua) do(ctx context.Context) error { + body, err := json.Marshal(map[string]interface{}{ + "runtime_id": c.runtimeID, + "name": c.deviceName, + "blueprint_id": c.blueprintID, + }) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/provisioning/lua_device", + Body: bytes.NewReader(body), + }) +} diff --git a/internal/app/enaptercli/cmd_provisioning_standalone.go b/internal/app/enaptercli/cmd_provisioning_standalone.go new file mode 100644 index 0000000..7c1fef2 --- /dev/null +++ b/internal/app/enaptercli/cmd_provisioning_standalone.go @@ -0,0 +1,63 @@ +package enaptercli + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdProvisioningStandalone struct { + cmdProvisioning + siteID string + deviceName string +} + +func buildCmdProvisioningStandalone() *cli.Command { + cmd := &cmdProvisioningStandalone{} + return &cli.Command{ + Name: "standalone", + Usage: "Create a new standalone device", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdProvisioningStandalone) Flags() []cli.Flag { + flags := c.cmdProvisioning.Flags() + return append(flags, &cli.StringFlag{ + Name: "site_id", + Aliases: []string{"s"}, + Usage: "site ID where to craate device", + Destination: &c.siteID, + Required: true, + }, &cli.StringFlag{ + Name: "device_name", + Aliases: []string{"n"}, + Usage: "name for a new device", + Destination: &c.deviceName, + Required: true, + }) +} + +func (c *cmdProvisioningStandalone) do(ctx context.Context) error { + body, err := json.Marshal(map[string]interface{}{ + "site_id": c.siteID, + "name": c.deviceName, + }) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/provisioning/standalone", + Body: bytes.NewReader(body), + }) +} diff --git a/internal/app/enaptercli/cmd_rules.go b/internal/app/enaptercli/cmd_rules.go deleted file mode 100644 index c6a465e..0000000 --- a/internal/app/enaptercli/cmd_rules.go +++ /dev/null @@ -1,30 +0,0 @@ -package enaptercli - -import "github.com/urfave/cli/v2" - -type cmdRules struct { - cmdBase - ruleID string -} - -func buildCmdRules() *cli.Command { - return &cli.Command{ - Name: "rules", - Usage: "Rules information and management commands.", - Subcommands: []*cli.Command{ - buildCmdRulesUpdate(), - buildCmdRulesLogs(), - }, - } -} - -func (c *cmdRules) Flags() []cli.Flag { - flags := c.cmdBase.Flags() - flags = append(flags, &cli.StringFlag{ - Name: "rule-id", - Usage: "Rule ID; can be obtained in cloud.enapter.com", - Required: true, - Destination: &c.ruleID, - }) - return flags -} diff --git a/internal/app/enaptercli/cmd_rules_logs.go b/internal/app/enaptercli/cmd_rules_logs.go deleted file mode 100644 index a02059e..0000000 --- a/internal/app/enaptercli/cmd_rules_logs.go +++ /dev/null @@ -1,43 +0,0 @@ -package enaptercli - -import ( - "context" - "fmt" - - "github.com/urfave/cli/v2" - - "github.com/enapter/enapter-cli/internal/cloudapi" -) - -type cmdRulesLogs struct { - cmdRules -} - -func buildCmdRulesLogs() *cli.Command { - cmd := &cmdRulesLogs{} - - return &cli.Command{ - Name: "logs", - Usage: "Stream logs from a rule", - CustomHelpTemplate: cmd.HelpTemplate(), - Flags: cmd.Flags(), - Before: cmd.Before, - Action: func(cliCtx *cli.Context) error { - return cmd.run(cliCtx.Context, cliCtx.App.Version) - }, - } -} - -func (c *cmdRulesLogs) run(ctx context.Context, version string) error { - writer := func(topic, msg string) { - fmt.Fprintf(c.writer, "[%s] %s\n", topic, msg) - } - - streamer, err := cloudapi.NewRuleLogsWriter(c.websocketsURL, c.token, - version, c.ruleID, writer) - if err != nil { - return fmt.Errorf("create streamer: %w", err) - } - - return streamer.Run(ctx) -} diff --git a/internal/app/enaptercli/cmd_rules_logs_test.go b/internal/app/enaptercli/cmd_rules_logs_test.go deleted file mode 100644 index 41e9339..0000000 --- a/internal/app/enaptercli/cmd_rules_logs_test.go +++ /dev/null @@ -1,41 +0,0 @@ -//nolint:dupl // not a duplicate of `devices logs` command tests -package enaptercli_test - -import ( - "strings" - "testing" -) - -func TestRuleLogs(t *testing.T) { - t.Run("simple", func(t *testing.T) { - inputFileName := "testdata/rules_logs/simple/input" - untilLinePrefix := "[info]" - expectedFileName := "testdata/rules_logs/simple/output" - testRuleLogs(t, inputFileName, untilLinePrefix, expectedFileName) - }) - - t.Run("invalid token", func(t *testing.T) { - inputFileName := "testdata/rules_logs/disconnect/invalid_token/input" - untilLinePrefix := "[connection]" - expectedFileName := "testdata/rules_logs/disconnect/invalid_token/output" - testRuleLogs(t, inputFileName, untilLinePrefix, expectedFileName) - }) - - t.Run("rule not found", func(t *testing.T) { - inputFileName := "testdata/rules_logs/disconnect/rule_not_found/input" - untilLinePrefix := "[connection] disconnected" - expectedFileName := "testdata/rules_logs/disconnect/rule_not_found/output" - testRuleLogs(t, inputFileName, untilLinePrefix, expectedFileName) - }) -} - -func testRuleLogs(t *testing.T, inputFileName, untilLinePrefix, expectedFileName string) { - const hardwareID = "SIM-RULE" - - identifier := map[string]string{"channel": "RuleChannel", "rule_id": hardwareID} - - command := strings.Split("enapter rules logs", " ") - command = append(command, "--rule-id", hardwareID) - - testLogsCommand(t, inputFileName, untilLinePrefix, expectedFileName, identifier, command) -} diff --git a/internal/app/enaptercli/cmd_rules_update.go b/internal/app/enaptercli/cmd_rules_update.go deleted file mode 100644 index b72a705..0000000 --- a/internal/app/enaptercli/cmd_rules_update.go +++ /dev/null @@ -1,107 +0,0 @@ -package enaptercli - -import ( - "context" - "fmt" - "net/http" - "os" - "time" - - "github.com/urfave/cli/v2" - - "github.com/enapter/enapter-cli/internal/cloudapi" -) - -const ruleUpdateDefaultTimeout = 30 * time.Second - -type cmdRulesUpdate struct { - cmdRules - path string - executionInterval int - stdlibVersion string - timeout time.Duration -} - -func buildCmdRulesUpdate() *cli.Command { - cmd := &cmdRulesUpdate{} - - return &cli.Command{ - Name: "update", - Usage: "Update rule.", - CustomHelpTemplate: cmd.HelpTemplate(), - Flags: cmd.Flags(), - Before: cmd.Before, - Action: func(cliCtx *cli.Context) error { - return cmd.run(cliCtx.Context, cliCtx.App.Version) - }, - } -} - -func (c *cmdRulesUpdate) Flags() []cli.Flag { - flags := c.cmdRules.Flags() - flags = append(flags, - &cli.StringFlag{ - Name: "rule-path", - Usage: "Path to file with rule Lua code", - Destination: &c.path, - }, - &cli.IntFlag{ - Name: "execution-interval", - Usage: "Rule execution interval in milliseconds", - DefaultText: "chosen by the server", - Destination: &c.executionInterval, - }, - &cli.StringFlag{ - Name: "stdlib-version", - Usage: "Version of standard library used by the rule", - DefaultText: "chosen by the server", - Destination: &c.stdlibVersion, - }, - &cli.DurationFlag{ - Name: "timeout", - Usage: "Time to wait for rule update", - Destination: &c.timeout, - Value: ruleUpdateDefaultTimeout, - }, - ) - return flags -} - -func (c *cmdRulesUpdate) run(ctx context.Context, version string) error { - if c.timeout != 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, c.timeout) - defer cancel() - } - - luaCode, err := os.ReadFile(c.path) - if err != nil { - return fmt.Errorf("read rule file: %w", err) - } - - transport := cloudapi.NewCredentialsTransport(http.DefaultTransport, c.token, version) - transport = cloudapi.NewCLIMessageWriterTransport(transport, &onceWriter{w: c.writer}) - client := cloudapi.NewClientWithURL(&http.Client{Transport: transport}, c.graphqlURL) - - input := cloudapi.UpdateRuleInput{ - RuleID: c.ruleID, - LuaCode: string(luaCode), - StdlibVersion: c.stdlibVersion, - ExecutionInterval: c.executionInterval, - } - - updateData, updateErrors, err := client.UpdateRule(ctx, input) - if err != nil { - return fmt.Errorf("do update: %w", err) - } - - if len(updateErrors) != 0 { - for _, e := range updateErrors { - fmt.Fprintln(c.writer, "[ERROR]", e.Message) - } - return errFinishedWithError - } - - fmt.Fprintln(c.writer, updateData.Message) - return nil -} diff --git a/internal/app/enaptercli/cmd_rules_update_test.go b/internal/app/enaptercli/cmd_rules_update_test.go deleted file mode 100644 index 0a7ee9c..0000000 --- a/internal/app/enaptercli/cmd_rules_update_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package enaptercli_test - -import ( - "encoding/json" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/bxcodec/faker/v3" - "github.com/stretchr/testify/require" -) - -func TestRulesUpdate(t *testing.T) { - testdataDir := "testdata/rules_update" - dirs, err := os.ReadDir(testdataDir) - require.NoError(t, err) - - for _, dir := range dirs { - if !dir.IsDir() { - continue - } - - dir := dir - t.Run(dir.Name(), func(t *testing.T) { - testRulesUpdate(t, filepath.Join(testdataDir, dir.Name())) - }) - } -} - -func TestRulesUpdateWrongFilePath(t *testing.T) { - args := strings.Split("enapter rules update --token token --rule-id ruleID "+ - "--gql-api-url apiURL --rule-path wrong", " ") - app := startTestApp(args...) - defer app.Stop() - - appErr := app.Wait() - require.EqualError(t, appErr, "read rule file: open wrong: no such file or directory") -} - -type rulesUpdateTestSettings struct { - RuleID string `json:"rule_id"` - RulePath string `json:"rule_path"` - Token string `json:"-"` -} - -func (s *rulesUpdateTestSettings) Fill(t *testing.T, dir string) { - settingsBytes, err := os.ReadFile(filepath.Join(dir, "settings.json")) - require.NoError(t, err) - require.NoError(t, json.Unmarshal(settingsBytes, s)) - - s.RulePath = filepath.Join(dir, s.RulePath) - s.Token = faker.Word() -} - -func testRulesUpdate(t *testing.T, dir string) { - var settings rulesUpdateTestSettings - settings.Fill(t, dir) - - reqs := byteSliceSliceFromFile(t, filepath.Join(dir, "requests")) - resps := byteSliceSliceFromFile(t, filepath.Join(dir, "responses")) - - srv := startTestServer(reqs, resps, "") - defer srv.Close() - - args := strings.Split("enapter rules update", " ") - args = append(args, - "--token", settings.Token, - "--rule-id", settings.RuleID, - "--rule-path", settings.RulePath, - "--gql-api-url", srv.URL) - - checkTestAppOutput(t, dir, args, reqs) -} diff --git a/internal/app/enaptercli/cmd_test.go b/internal/app/enaptercli/cmd_test.go deleted file mode 100644 index fde7025..0000000 --- a/internal/app/enaptercli/cmd_test.go +++ /dev/null @@ -1,137 +0,0 @@ -package enaptercli_test - -import ( - "bytes" - "io" - "net/http" - "net/http/httptest" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/require" -) - -type byteSliceSlice struct { - lines [][]byte -} - -func byteSliceSliceFromFile(t *testing.T, path string) *byteSliceSlice { - f, err := os.ReadFile(path) - require.NoError(t, err) - lines := bytes.Split(f, []byte{'\n'}) - - n := 0 - for _, line := range lines { - if len(line) > 0 { - lines[n] = line - n++ - } - } - - return &byteSliceSlice{lines: lines[:n]} -} - -func (b *byteSliceSlice) Next() []byte { - for i, s := range b.lines { - b.lines = b.lines[i+1:] - return s - } - return nil -} - -func (b *byteSliceSlice) Append(d []byte) { - b.lines = append(b.lines, d) -} - -func (b *byteSliceSlice) Buffer() [][]byte { - return b.lines -} - -func (b *byteSliceSlice) Clear() { - b.lines = nil -} - -func startTestServer(reqs, resps *byteSliceSlice, cliMessage string) *httptest.Server { - if update { - reqs.Clear() - } - - handler := func(w http.ResponseWriter, r *http.Request) { - resp := resps.Next() - if resp == nil { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte("to much requests for test (not enough responses)")) - return - } - - var req []byte - if !update { - req = reqs.Next() - if len(req) == 0 { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte("to much requests for test (not enough requests)")) - return - } - } - - if cliMessage != "" { - w.Header().Set("X-ENAPTER-CLI-MESSAGE", cliMessage) - } - - reqBody, err := io.ReadAll(r.Body) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - _, _ = w.Write([]byte("failed to read request")) - return - } - - if update { - reqs.Append(reqBody) - } else { - reqBody := bytes.TrimRight(reqBody, "\n") - if !bytes.Equal(reqBody, req) { - w.WriteHeader(http.StatusBadRequest) - _, _ = w.Write([]byte("unexpected request\nActual\n")) - _, _ = w.Write(reqBody) - _, _ = w.Write([]byte("\nExpected\n")) - _, _ = w.Write(req) - return - } - } - - _, _ = w.Write(resp) - } - - return httptest.NewServer(http.HandlerFunc(handler)) -} - -func checkTestAppOutput(t *testing.T, basePath string, args []string, requests *byteSliceSlice) { - app := startTestApp(args...) - defer app.Stop() - - appErr := app.Wait() - - actual, err := io.ReadAll(app.Stdout()) - require.NoError(t, err) - - if appErr != nil { - actual = append(actual, []byte("app exit with error: "+appErr.Error()+"\n")...) - } - - expectedFileName := filepath.Join(basePath, "output") - if update { - err := os.WriteFile(expectedFileName, actual, 0o600) - require.NoError(t, err) - - requestsFileName := filepath.Join(basePath, "requests") - requestsBytes := bytes.Join(requests.Buffer(), []byte{'\n'}) - err = os.WriteFile(requestsFileName, requestsBytes, 0o600) - require.NoError(t, err) - } - - expected, err := os.ReadFile(expectedFileName) - require.NoError(t, err) - - require.Equal(t, string(expected), string(actual)) -} diff --git a/internal/app/enaptercli/errors.go b/internal/app/enaptercli/errors.go index e2d3fb3..947727b 100644 --- a/internal/app/enaptercli/errors.go +++ b/internal/app/enaptercli/errors.go @@ -3,7 +3,9 @@ package enaptercli import "errors" var ( - errFinishedWithError = errors.New("request execution failed") - errAPITokenMissed = errors.New("API token missing. Set it up using environment " + + errAPITokenMissed = errors.New("API token missing. Set it up using environment " + "variable ENAPTER_API_TOKEN") + errBlueprintIDMissed = errors.New("blueprint ID is missed") + errBlueprintPathMissed = errors.New("blueprint path is missed") + errUnsupportedFlagValue = errors.New("unsupported flag value") ) diff --git a/internal/app/enaptercli/execute.go b/internal/app/enaptercli/execute.go index 09df97a..a873b37 100644 --- a/internal/app/enaptercli/execute.go +++ b/internal/app/enaptercli/execute.go @@ -3,6 +3,7 @@ package enaptercli import ( "archive/zip" "bytes" + "fmt" "io" "os" "path/filepath" @@ -17,12 +18,12 @@ func NewApp() *cli.App { app.Usage = "Command line interface for Enapter services." app.Description = "Enapter CLI requires access token for authentication. " + - "The token can be obtained in your Enapter Cloud account settings.\n\n" + - "Configure API token using ENAPTER_API_TOKEN environment variable or using --token global option." + "The token can be obtained in your Enapter Cloud account settings." app.Commands = []*cli.Command{ buildCmdDevices(), - buildCmdRules(), + buildCmdBlueprints(), + buildCmdProvisioning(), } return app @@ -30,27 +31,29 @@ func NewApp() *cli.App { func zipDir(path string) ([]byte, error) { buf := &bytes.Buffer{} - myZip := zip.NewWriter(buf) + zw := zip.NewWriter(buf) - path = strings.TrimPrefix(path, "./") - - err := filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error { + err := filepath.WalkDir(path, func(filePath string, entry os.DirEntry, err error) error { if err != nil { return err } - if info.IsDir() { + if entry.IsDir() { return nil } + relPath := strings.TrimPrefix(filePath, path) relPath = strings.TrimPrefix(relPath, "/") - zipFile, err := myZip.Create(relPath) + zipFile, err := zw.Create(relPath) if err != nil { return err } + fsFile, err := os.Open(filePath) if err != nil { return err } + defer fsFile.Close() + _, err = io.Copy(zipFile, fsFile) if err != nil { return err @@ -58,12 +61,12 @@ func zipDir(path string) ([]byte, error) { return nil }) if err != nil { - return nil, err + return nil, fmt.Errorf("walk dir: %w", err) } - if err := myZip.Close(); err != nil { - return nil, err + if err := zw.Close(); err != nil { + return nil, fmt.Errorf("close zip: %w", err) } - return buf.Bytes(), err + return buf.Bytes(), nil } diff --git a/internal/app/enaptercli/once_writer.go b/internal/app/enaptercli/once_writer.go deleted file mode 100644 index 4ae3a3e..0000000 --- a/internal/app/enaptercli/once_writer.go +++ /dev/null @@ -1,20 +0,0 @@ -package enaptercli - -import ( - "io" - "sync" -) - -type onceWriter struct { - once sync.Once - w io.Writer -} - -func (w *onceWriter) Write(p []byte) (int, error) { - n := len(p) - var err error - w.once.Do(func() { - n, err = w.w.Write(p) - }) - return n, err -} diff --git a/internal/app/enaptercli/testdata/device_execute/error/output b/internal/app/enaptercli/testdata/device_execute/error/output deleted file mode 100644 index a0772af..0000000 --- a/internal/app/enaptercli/testdata/device_execute/error/output +++ /dev/null @@ -1 +0,0 @@ -app exit with error: forbidden: Access denied. diff --git a/internal/app/enaptercli/testdata/device_execute/error/responses b/internal/app/enaptercli/testdata/device_execute/error/responses deleted file mode 100644 index c1bed99..0000000 --- a/internal/app/enaptercli/testdata/device_execute/error/responses +++ /dev/null @@ -1 +0,0 @@ -{"errors":[{"code":"forbidden","message":"Access denied."}]} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/device_execute/progress/output b/internal/app/enaptercli/testdata/device_execute/progress/output deleted file mode 100644 index a267617..0000000 --- a/internal/app/enaptercli/testdata/device_execute/progress/output +++ /dev/null @@ -1,3 +0,0 @@ -{"state":"started"} -{"state":"device_in_progress","payload":{"progress":50}} -{"state":"succeeded"} diff --git a/internal/app/enaptercli/testdata/device_execute/progress/responses b/internal/app/enaptercli/testdata/device_execute/progress/responses deleted file mode 100644 index dbb2c43..0000000 --- a/internal/app/enaptercli/testdata/device_execute/progress/responses +++ /dev/null @@ -1,3 +0,0 @@ -{"state":"started"} -{"state":"device_in_progress","payload":{"progress":50}} -{"state":"succeeded"} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/device_execute/simple/output b/internal/app/enaptercli/testdata/device_execute/simple/output deleted file mode 100644 index 7d416b3..0000000 --- a/internal/app/enaptercli/testdata/device_execute/simple/output +++ /dev/null @@ -1 +0,0 @@ -{"state":"started"} diff --git a/internal/app/enaptercli/testdata/device_execute/simple/responses b/internal/app/enaptercli/testdata/device_execute/simple/responses deleted file mode 100644 index 78d59d2..0000000 --- a/internal/app/enaptercli/testdata/device_execute/simple/responses +++ /dev/null @@ -1,2 +0,0 @@ -{"state":"started"} -{"state":"succeeded"} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/device_logs/disconnect/device_not_found/input b/internal/app/enaptercli/testdata/device_logs/disconnect/device_not_found/input deleted file mode 100644 index 20dbe46..0000000 --- a/internal/app/enaptercli/testdata/device_logs/disconnect/device_not_found/input +++ /dev/null @@ -1,5 +0,0 @@ -{"type":"welcome"} - -{"identifier":"{\"channel\":\"DeviceChannel\",\"hardware_id\":\"SIM-WTM\"}","type":"message","message":{"topic":"error","payload":"Device not found"}} - -{"identifier":"{\"channel\":\"DeviceChannel\",\"hardware_id\":\"SIM-WTM\"}","type":"reject_subscription"} diff --git a/internal/app/enaptercli/testdata/device_logs/disconnect/device_not_found/output b/internal/app/enaptercli/testdata/device_logs/disconnect/device_not_found/output deleted file mode 100644 index cae285f..0000000 --- a/internal/app/enaptercli/testdata/device_logs/disconnect/device_not_found/output +++ /dev/null @@ -1,3 +0,0 @@ -[connection] welcome -[error] Device not found -[connection] disconnected diff --git a/internal/app/enaptercli/testdata/device_logs/disconnect/invalid_token/input b/internal/app/enaptercli/testdata/device_logs/disconnect/invalid_token/input deleted file mode 100644 index 37f6889..0000000 --- a/internal/app/enaptercli/testdata/device_logs/disconnect/invalid_token/input +++ /dev/null @@ -1 +0,0 @@ -{"type":"disconnect","reason":"unauthorized","reconnect":false} diff --git a/internal/app/enaptercli/testdata/device_logs/disconnect/invalid_token/output b/internal/app/enaptercli/testdata/device_logs/disconnect/invalid_token/output deleted file mode 100644 index 1c44604..0000000 --- a/internal/app/enaptercli/testdata/device_logs/disconnect/invalid_token/output +++ /dev/null @@ -1 +0,0 @@ -[connection] disconnected with reason: unauthorized diff --git a/internal/app/enaptercli/testdata/device_logs/simple/input b/internal/app/enaptercli/testdata/device_logs/simple/input deleted file mode 100644 index 63189d3..0000000 --- a/internal/app/enaptercli/testdata/device_logs/simple/input +++ /dev/null @@ -1,16 +0,0 @@ -{"type":"welcome"} -{"identifier":"{\"channel\":\"DeviceChannel\",\"hardware_id\":\"SIM-WTM\"}","type":"confirm_subscription"} - -{"type":"message","identifier":"{\"channel\":\"DeviceChannel\",\"hardware_id\":\"SIM-WTM\"}","message":{"topic":"register","payload":"{\"timestamp\":1606485742,\"fw_ver\":\"1.0.0\",\"efuse\":\"0x1C04\",\"product_revision\":\"WTM21 REV1\",\"vendor\":\"Enapter\",\"model\":\"WTM\"}"}} - -{"type":"ping","message":"1606485743"} - -{"type":"message","identifier":"{\"channel\":\"DeviceChannel\",\"hardware_id\":\"SIM-FRANK\"}","message":{"topic":"telemetry","payload":"{\"timestamp\":1606485747,\"status\":\"ok\",\"PUMP_out_power\":false,\"SV01_out_open\":false,\"LSH_in\":false,\"LSL_in\":false,\"WPS01_in\":false,\"BUTTON_in\":false,\"TT01_in_c\":2.0,\"TT02_in_c\":1.0,\"CS01_in_v\":4.0,\"CS01_in_a\":3.0,\"CT01_in_v\":6.0,\"CT01_in_uscm\":5.0,\"CT01_in_uscm_comp\":9.0,\"LT01_in_v\":8.0,\"LT01_in_l\":7.0,\"last_calibration\":1611756147}"}} - -{"type":"ping","message":"1606485746"} - -{"type":"message","identifier":"{\"channel\":\"OtherChannel\",\"hardware_id\":\"SIM-WTM\"}","message":{"topic":"telemetry","payload":"{\"timestamp\":1606485747,\"status\":\"ok\",\"PUMP_out_power\":false,\"SV01_out_open\":false,\"LSH_in\":false,\"LSL_in\":false,\"WPS01_in\":false,\"BUTTON_in\":false,\"TT01_in_c\":2.0,\"TT02_in_c\":1.0,\"CS01_in_v\":4.0,\"CS01_in_a\":3.0,\"CT01_in_v\":6.0,\"CT01_in_uscm\":5.0,\"CT01_in_uscm_comp\":9.0,\"LT01_in_v\":8.0,\"LT01_in_l\":7.0,\"last_calibration\":1611756147}"}} - -{"type":"ping","message":"1606485749"} - -{"type":"message","identifier":"{\"hardware_id\":\"SIM-WTM\",\"channel\":\"DeviceChannel\"}","message":{"topic":"telemetry","payload":"{\"timestamp\":1606486092,\"status\":\"ok\",\"PUMP_out_power\":false,\"SV01_out_open\":false,\"LSH_in\":false,\"LSL_in\":false,\"WPS01_in\":false,\"BUTTON_in\":false,\"TT01_in_c\":1.0,\"TT02_in_c\":2.0,\"CS01_in_v\":3.0,\"CS01_in_a\":4.0,\"CT01_in_v\":5.0,\"CT01_in_uscm\":6.0,\"CT01_in_uscm_comp\":10.0,\"LT01_in_v\":7.0,\"LT01_in_l\":8.0,\"last_calibration\":1611756492}"}} diff --git a/internal/app/enaptercli/testdata/device_logs/simple/output b/internal/app/enaptercli/testdata/device_logs/simple/output deleted file mode 100644 index 3277fb5..0000000 --- a/internal/app/enaptercli/testdata/device_logs/simple/output +++ /dev/null @@ -1,6 +0,0 @@ -[connection] welcome -[connection] confirm_subscription -[register] {"timestamp":1606485742,"fw_ver":"1.0.0","efuse":"0x1C04","product_revision":"WTM21 REV1","vendor":"Enapter","model":"WTM"} -[read_error] skip message with unknown identifier map[channel:DeviceChannel hardware_id:SIM-FRANK] -[read_error] skip message with unknown identifier map[channel:OtherChannel hardware_id:SIM-WTM] -[telemetry] {"timestamp":1606486092,"status":"ok","PUMP_out_power":false,"SV01_out_open":false,"LSH_in":false,"LSL_in":false,"WPS01_in":false,"BUTTON_in":false,"TT01_in_c":1.0,"TT02_in_c":2.0,"CS01_in_v":3.0,"CS01_in_a":4.0,"CT01_in_v":5.0,"CT01_in_uscm":6.0,"CT01_in_uscm_comp":10.0,"LT01_in_v":7.0,"LT01_in_l":8.0,"last_calibration":1611756492} diff --git a/internal/app/enaptercli/testdata/device_upload/cli_message/output b/internal/app/enaptercli/testdata/device_upload/cli_message/output deleted file mode 100644 index 1b92681..0000000 --- a/internal/app/enaptercli/testdata/device_upload/cli_message/output +++ /dev/null @@ -1,9 +0,0 @@ -Blueprint files to be uploaded: -* testdata/device_upload/simple/blueprint/manifest.yml -VERSION IS OUTDATED -upload started with operation id 25 -[#25] 2020-12-09T14:02:07Z [INFO] Started uploading blueprint[id=d428e77c-3081-4873-b343-2f8f96d9cadc] on device[hardware_id=SIM-WTM] -[#25] 2020-12-09T14:02:07Z [INFO] Generating configuration for uploading -[#25] 2020-12-09T14:02:07Z [INFO] Updating configuration on the platform -[#25] 2020-12-09T14:02:07Z [INFO] Uploading blueprint finished successfully -Done! diff --git a/internal/app/enaptercli/testdata/device_upload/cli_message/requests b/internal/app/enaptercli/testdata/device_upload/cli_message/requests deleted file mode 100644 index 8efcf61..0000000 --- a/internal/app/enaptercli/testdata/device_upload/cli_message/requests +++ /dev/null @@ -1,7 +0,0 @@ -{"query":"mutation($input:UploadBlueprintInput!){device{uploadBlueprint(input: $input){data{code,message,title,operationId},errors{code,message,path,title}}}}","variables":{"input":{"blueprint":"data:application/gzip;base64,UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAMAAAAbWFuaWZlc3QueW1sAQAA//9QSwcIAAAAAAUAAAAAAAAAUEsBAhQAFAAIAAgAAAAAAAAAAAAFAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAG1hbmlmZXN0LnltbFBLBQYAAAAAAQABADoAAAA/AAAAAAA=","hardwareId":"SIM-WTM"}}} - -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"","hardware_id":"SIM-WTM","operation_id":"25"}} - -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"MQ","hardware_id":"SIM-WTM","operation_id":"25"}} - -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"NA","hardware_id":"SIM-WTM","operation_id":"25"}} diff --git a/internal/app/enaptercli/testdata/device_upload/cli_message/responses b/internal/app/enaptercli/testdata/device_upload/cli_message/responses deleted file mode 100644 index cefce05..0000000 --- a/internal/app/enaptercli/testdata/device_upload/cli_message/responses +++ /dev/null @@ -1,4 +0,0 @@ -{"data":{"device":{"uploadBlueprint":{"data":{"code":"started","message":"Uploading blueprint successfully started.","title":"Started","operationId":"25"},"errors":null}}}} -{"data":{"device":{"blueprintUpdateOperation":{"status":"IN_PROGRESS","logs":{"edges":[{"cursor":"MQ","node":{"payload":"Started uploading blueprint[id=d428e77c-3081-4873-b343-2f8f96d9cadc] on device[hardware_id=SIM-WTM]","createdAt":"2020-12-09T14:02:07Z","severity":"INFO"}}]}}}}} -{"data":{"device":{"blueprintUpdateOperation":{"status":"SUCCEEDED","logs":{"edges":[{"cursor":"Mg","node":{"payload":"Generating configuration for uploading","createdAt":"2020-12-09T14:02:07Z","severity":"INFO"}},{"cursor":"Mw","node":{"payload":"Updating configuration on the platform","createdAt":"2020-12-09T14:02:07Z","severity":"INFO"}},{"cursor":"NA","node":{"payload":"Uploading blueprint finished successfully","createdAt":"2020-12-09T14:02:07Z","severity":"INFO"}}]}}}}} -{"data":{"device":{"blueprintUpdateOperation":{"status":"SUCCEEDED","logs":{"edges":[]}}}}} diff --git a/internal/app/enaptercli/testdata/device_upload/cli_message/settings.json b/internal/app/enaptercli/testdata/device_upload/cli_message/settings.json deleted file mode 100644 index 18d48a5..0000000 --- a/internal/app/enaptercli/testdata/device_upload/cli_message/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "hardware_id": "SIM-WTM", - "cli_message": "VERSION IS OUTDATED" -} diff --git a/internal/app/enaptercli/testdata/device_upload/simple/blueprint/manifest.yml b/internal/app/enaptercli/testdata/device_upload/simple/blueprint/manifest.yml deleted file mode 100644 index e69de29..0000000 diff --git a/internal/app/enaptercli/testdata/device_upload/simple/output b/internal/app/enaptercli/testdata/device_upload/simple/output deleted file mode 100644 index 40f2098..0000000 --- a/internal/app/enaptercli/testdata/device_upload/simple/output +++ /dev/null @@ -1,8 +0,0 @@ -Blueprint files to be uploaded: -* testdata/device_upload/simple/blueprint/manifest.yml -upload started with operation id 25 -[#25] 2020-12-09T14:02:07Z [INFO] Started uploading blueprint[id=d428e77c-3081-4873-b343-2f8f96d9cadc] on device[hardware_id=SIM-WTM] -[#25] 2020-12-09T14:02:07Z [INFO] Generating configuration for uploading -[#25] 2020-12-09T14:02:07Z [INFO] Updating configuration on the platform -[#25] 2020-12-09T14:02:07Z [INFO] Uploading blueprint finished successfully -Done! diff --git a/internal/app/enaptercli/testdata/device_upload/simple/requests b/internal/app/enaptercli/testdata/device_upload/simple/requests deleted file mode 100644 index 8efcf61..0000000 --- a/internal/app/enaptercli/testdata/device_upload/simple/requests +++ /dev/null @@ -1,7 +0,0 @@ -{"query":"mutation($input:UploadBlueprintInput!){device{uploadBlueprint(input: $input){data{code,message,title,operationId},errors{code,message,path,title}}}}","variables":{"input":{"blueprint":"data:application/gzip;base64,UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAMAAAAbWFuaWZlc3QueW1sAQAA//9QSwcIAAAAAAUAAAAAAAAAUEsBAhQAFAAIAAgAAAAAAAAAAAAFAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAG1hbmlmZXN0LnltbFBLBQYAAAAAAQABADoAAAA/AAAAAAA=","hardwareId":"SIM-WTM"}}} - -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"","hardware_id":"SIM-WTM","operation_id":"25"}} - -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"MQ","hardware_id":"SIM-WTM","operation_id":"25"}} - -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"NA","hardware_id":"SIM-WTM","operation_id":"25"}} diff --git a/internal/app/enaptercli/testdata/device_upload/simple/responses b/internal/app/enaptercli/testdata/device_upload/simple/responses deleted file mode 100644 index cefce05..0000000 --- a/internal/app/enaptercli/testdata/device_upload/simple/responses +++ /dev/null @@ -1,4 +0,0 @@ -{"data":{"device":{"uploadBlueprint":{"data":{"code":"started","message":"Uploading blueprint successfully started.","title":"Started","operationId":"25"},"errors":null}}}} -{"data":{"device":{"blueprintUpdateOperation":{"status":"IN_PROGRESS","logs":{"edges":[{"cursor":"MQ","node":{"payload":"Started uploading blueprint[id=d428e77c-3081-4873-b343-2f8f96d9cadc] on device[hardware_id=SIM-WTM]","createdAt":"2020-12-09T14:02:07Z","severity":"INFO"}}]}}}}} -{"data":{"device":{"blueprintUpdateOperation":{"status":"SUCCEEDED","logs":{"edges":[{"cursor":"Mg","node":{"payload":"Generating configuration for uploading","createdAt":"2020-12-09T14:02:07Z","severity":"INFO"}},{"cursor":"Mw","node":{"payload":"Updating configuration on the platform","createdAt":"2020-12-09T14:02:07Z","severity":"INFO"}},{"cursor":"NA","node":{"payload":"Uploading blueprint finished successfully","createdAt":"2020-12-09T14:02:07Z","severity":"INFO"}}]}}}}} -{"data":{"device":{"blueprintUpdateOperation":{"status":"SUCCEEDED","logs":{"edges":[]}}}}} diff --git a/internal/app/enaptercli/testdata/device_upload/simple/settings.json b/internal/app/enaptercli/testdata/device_upload/simple/settings.json deleted file mode 100644 index e2bb1f5..0000000 --- a/internal/app/enaptercli/testdata/device_upload/simple/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "hardware_id": "SIM-WTM" -} diff --git a/internal/app/enaptercli/testdata/device_upload/upload_errors/output b/internal/app/enaptercli/testdata/device_upload/upload_errors/output deleted file mode 100644 index 8ba1c83..0000000 --- a/internal/app/enaptercli/testdata/device_upload/upload_errors/output +++ /dev/null @@ -1,5 +0,0 @@ -Blueprint files to be uploaded: -* testdata/device_upload/simple/blueprint/manifest.yml -[ERROR] hmm... wait a minute -[ERROR] oops! -app exit with error: request execution failed diff --git a/internal/app/enaptercli/testdata/device_upload/upload_errors/requests b/internal/app/enaptercli/testdata/device_upload/upload_errors/requests deleted file mode 100644 index e74f0f1..0000000 --- a/internal/app/enaptercli/testdata/device_upload/upload_errors/requests +++ /dev/null @@ -1 +0,0 @@ -{"query":"mutation($input:UploadBlueprintInput!){device{uploadBlueprint(input: $input){data{code,message,title,operationId},errors{code,message,path,title}}}}","variables":{"input":{"blueprint":"data:application/gzip;base64,UEsDBBQACAAIAAAAAAAAAAAAAAAAAAAAAAAMAAAAbWFuaWZlc3QueW1sAQAA//9QSwcIAAAAAAUAAAAAAAAAUEsBAhQAFAAIAAgAAAAAAAAAAAAFAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAG1hbmlmZXN0LnltbFBLBQYAAAAAAQABADoAAAA/AAAAAAA=","hardwareId":"SIM-WTM"}}} diff --git a/internal/app/enaptercli/testdata/device_upload/upload_errors/responses b/internal/app/enaptercli/testdata/device_upload/upload_errors/responses deleted file mode 100644 index 31dfdfd..0000000 --- a/internal/app/enaptercli/testdata/device_upload/upload_errors/responses +++ /dev/null @@ -1 +0,0 @@ -{"data":{"device":{"uploadBlueprint":{"data":null,"errors":[{"code":"warning","message":"hmm... wait a minute","title":"Started"},{"code":"fatal","message":"oops!","title":"Started"}]}}}} diff --git a/internal/app/enaptercli/testdata/device_upload/upload_errors/settings.json b/internal/app/enaptercli/testdata/device_upload/upload_errors/settings.json deleted file mode 100644 index e2bb1f5..0000000 --- a/internal/app/enaptercli/testdata/device_upload/upload_errors/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "hardware_id": "SIM-WTM" -} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/cli_message/output b/internal/app/enaptercli/testdata/device_upload_logs/cli_message/output deleted file mode 100644 index a4cd9d1..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/cli_message/output +++ /dev/null @@ -1,5 +0,0 @@ -VERSION IS OUTDATED -[#5] 2020-12-17T13:32:57Z [INFO] Started uploading blueprint[id=42cc8af1-cc60-4eeb-972f-0c0bfa6e3df5] on device[hardware_id=SIM-WTM] -[#5] 2020-12-17T13:32:57Z [INFO] Generating configuration for uploading -[#5] 2020-12-17T13:32:57Z [INFO] Updating configuration on the platform -[#5] 2020-12-17T13:32:57Z [INFO] Uploading blueprint finished successfully diff --git a/internal/app/enaptercli/testdata/device_upload_logs/cli_message/requests b/internal/app/enaptercli/testdata/device_upload_logs/cli_message/requests deleted file mode 100644 index d2f87b8..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/cli_message/requests +++ /dev/null @@ -1,3 +0,0 @@ -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"","hardware_id":"SIM-WTM","operation_id":"5"}} - -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"NA","hardware_id":"SIM-WTM","operation_id":"5"}} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/cli_message/responses b/internal/app/enaptercli/testdata/device_upload_logs/cli_message/responses deleted file mode 100644 index 5588761..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/cli_message/responses +++ /dev/null @@ -1,2 +0,0 @@ -{"data":{"device":{"blueprintUpdateOperation":{"status":"SUCCEEDED","logs":{"edges":[{"cursor":"MQ","node":{"createdAt":"2020-12-17T13:32:57Z","payload":"Started uploading blueprint[id=42cc8af1-cc60-4eeb-972f-0c0bfa6e3df5] on device[hardware_id=SIM-WTM]","severity":"INFO"}},{"cursor":"Mg","node":{"createdAt":"2020-12-17T13:32:57Z","payload":"Generating configuration for uploading","severity":"INFO"}},{"cursor":"Mw","node":{"createdAt":"2020-12-17T13:32:57Z","payload":"Updating configuration on the platform","severity":"INFO"}},{"cursor":"NA","node":{"createdAt":"2020-12-17T13:32:57Z","payload":"Uploading blueprint finished successfully","severity":"INFO"}}]}}}}} -{"data":{"device":{"blueprintUpdateOperation":{"status":"SUCCEEDED","logs":{"edges":[]}}}}} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/cli_message/settings.json b/internal/app/enaptercli/testdata/device_upload_logs/cli_message/settings.json deleted file mode 100644 index 20c3a13..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/cli_message/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "hardware_id": "SIM-WTM", - "operation_id": "5", - "cli_message": "VERSION IS OUTDATED" -} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/simple/output b/internal/app/enaptercli/testdata/device_upload_logs/simple/output deleted file mode 100644 index 39a229e..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/simple/output +++ /dev/null @@ -1,4 +0,0 @@ -[#5] 2020-12-17T13:32:57Z [INFO] Started uploading blueprint[id=42cc8af1-cc60-4eeb-972f-0c0bfa6e3df5] on device[hardware_id=SIM-WTM] -[#5] 2020-12-17T13:32:57Z [INFO] Generating configuration for uploading -[#5] 2020-12-17T13:32:57Z [INFO] Updating configuration on the platform -[#5] 2020-12-17T13:32:57Z [INFO] Uploading blueprint finished successfully diff --git a/internal/app/enaptercli/testdata/device_upload_logs/simple/requests b/internal/app/enaptercli/testdata/device_upload_logs/simple/requests deleted file mode 100644 index d2f87b8..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/simple/requests +++ /dev/null @@ -1,3 +0,0 @@ -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"","hardware_id":"SIM-WTM","operation_id":"5"}} - -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"NA","hardware_id":"SIM-WTM","operation_id":"5"}} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/simple/responses b/internal/app/enaptercli/testdata/device_upload_logs/simple/responses deleted file mode 100644 index 5588761..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/simple/responses +++ /dev/null @@ -1,2 +0,0 @@ -{"data":{"device":{"blueprintUpdateOperation":{"status":"SUCCEEDED","logs":{"edges":[{"cursor":"MQ","node":{"createdAt":"2020-12-17T13:32:57Z","payload":"Started uploading blueprint[id=42cc8af1-cc60-4eeb-972f-0c0bfa6e3df5] on device[hardware_id=SIM-WTM]","severity":"INFO"}},{"cursor":"Mg","node":{"createdAt":"2020-12-17T13:32:57Z","payload":"Generating configuration for uploading","severity":"INFO"}},{"cursor":"Mw","node":{"createdAt":"2020-12-17T13:32:57Z","payload":"Updating configuration on the platform","severity":"INFO"}},{"cursor":"NA","node":{"createdAt":"2020-12-17T13:32:57Z","payload":"Uploading blueprint finished successfully","severity":"INFO"}}]}}}}} -{"data":{"device":{"blueprintUpdateOperation":{"status":"SUCCEEDED","logs":{"edges":[]}}}}} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/simple/settings.json b/internal/app/enaptercli/testdata/device_upload_logs/simple/settings.json deleted file mode 100644 index af8682b..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/simple/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "hardware_id": "SIM-WTM", - "operation_id": "5" -} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/output b/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/output deleted file mode 100644 index 7367de8..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/output +++ /dev/null @@ -1 +0,0 @@ -app exit with error: request execution failed: device not found diff --git a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/requests b/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/requests deleted file mode 100644 index 450f634..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/requests +++ /dev/null @@ -1 +0,0 @@ -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"","hardware_id":"SIM-WTM","operation_id":"54"}} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/responses b/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/responses deleted file mode 100644 index e1c8cf9..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/responses +++ /dev/null @@ -1 +0,0 @@ -{"data":{"device": null}} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/settings.json b/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/settings.json deleted file mode 100644 index f5b1cbf..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "hardware_id": "SIM-WTM", - "operation_id": "54" -} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/output b/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/output deleted file mode 100644 index 7367de8..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/output +++ /dev/null @@ -1 +0,0 @@ -app exit with error: request execution failed: device not found diff --git a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/requests b/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/requests deleted file mode 100644 index b8be040..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/requests +++ /dev/null @@ -1 +0,0 @@ -{"query":"query($hardware_id:ID!$last_int:Int!){device(hardwareId: $hardware_id){blueprintUpdateOperations(last: $last_int){nodes{id}}}}","variables":{"hardware_id":"SIM-WTM","last_int":2}} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/responses b/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/responses deleted file mode 100644 index e1c8cf9..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/responses +++ /dev/null @@ -1 +0,0 @@ -{"data":{"device": null}} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/settings.json b/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/settings.json deleted file mode 100644 index e2bb1f5..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/unknown_hardware_id_without_operation_id/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "hardware_id": "SIM-WTM" -} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/output b/internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/output deleted file mode 100644 index 8f6a001..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/output +++ /dev/null @@ -1 +0,0 @@ -app exit with error: request execution failed: operation not found diff --git a/internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/requests b/internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/requests deleted file mode 100644 index 450f634..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/requests +++ /dev/null @@ -1 +0,0 @@ -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"","hardware_id":"SIM-WTM","operation_id":"54"}} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/responses b/internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/responses deleted file mode 100644 index 692d3f2..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/responses +++ /dev/null @@ -1 +0,0 @@ -{"data":{"device": {"blueprintUpdateOperation":null}}} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/settings.json b/internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/settings.json deleted file mode 100644 index f5b1cbf..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/unknown_operation_id/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "hardware_id": "SIM-WTM", - "operation_id": "54" -} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/output b/internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/output deleted file mode 100644 index 2502ef5..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/output +++ /dev/null @@ -1,8 +0,0 @@ -[#17] 2020-12-17T16:07:37Z [INFO] Started uploading blueprint[id=84606e67-d377-4f11-b3e4-421f08265ec7] on device[hardware_id=SIM-WTM] -[#17] 2020-12-17T16:07:37Z [INFO] Generating configuration for uploading -[#17] 2020-12-17T16:07:37Z [INFO] Updating configuration on the platform -[#17] 2020-12-17T16:07:37Z [INFO] Uploading blueprint finished successfully -[#20] 2020-12-21T11:53:14Z [INFO] Started uploading blueprint[id=a7be3c13-e138-4c43-a7c0-db50ef775613] on device[hardware_id=SIM-WTM] -[#20] 2020-12-21T11:53:14Z [INFO] Generating configuration for uploading -[#20] 2020-12-21T11:53:14Z [INFO] Updating configuration on the platform -[#20] 2020-12-21T11:53:14Z [INFO] Uploading blueprint finished successfully diff --git a/internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/requests b/internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/requests deleted file mode 100644 index 52f4479..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/requests +++ /dev/null @@ -1,9 +0,0 @@ -{"query":"query($hardware_id:ID!$last_int:Int!){device(hardwareId: $hardware_id){blueprintUpdateOperations(last: $last_int){nodes{id}}}}","variables":{"hardware_id":"SIM-WTM","last_int":2}} - -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"","hardware_id":"SIM-WTM","operation_id":"17"}} - -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"NA","hardware_id":"SIM-WTM","operation_id":"17"}} - -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"","hardware_id":"SIM-WTM","operation_id":"20"}} - -{"query":"query($after_cursor:String!$hardware_id:ID!$operation_id:ID!){device(hardwareId: $hardware_id){blueprintUpdateOperation(id: $operation_id){status,logs(after: $after_cursor){edges{cursor,node{payload,createdAt,severity}}}}}}","variables":{"after_cursor":"NA","hardware_id":"SIM-WTM","operation_id":"20"}} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/responses b/internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/responses deleted file mode 100644 index 8127b73..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/responses +++ /dev/null @@ -1,5 +0,0 @@ -{"data":{"device":{"blueprintUpdateOperations":{"nodes":[{"id":"17"},{"id":"20"}]}}}} -{"data":{"device":{"blueprintUpdateOperation":{"status":"SUCCEEDED","logs":{"edges":[{"cursor":"MQ","node":{"createdAt":"2020-12-17T16:07:37Z","payload":"Started uploading blueprint[id=84606e67-d377-4f11-b3e4-421f08265ec7] on device[hardware_id=SIM-WTM]","severity":"INFO"}},{"cursor":"Mg","node":{"createdAt":"2020-12-17T16:07:37Z","payload":"Generating configuration for uploading","severity":"INFO"}},{"cursor":"Mw","node":{"createdAt":"2020-12-17T16:07:37Z","payload":"Updating configuration on the platform","severity":"INFO"}},{"cursor":"NA","node":{"createdAt":"2020-12-17T16:07:37Z","payload":"Uploading blueprint finished successfully","severity":"INFO"}}]}}}}} -{"data":{"device":{"blueprintUpdateOperation":{"status":"SUCCEEDED","logs":{"edges":[]}}}}} -{"data":{"device":{"blueprintUpdateOperation":{"status":"SUCCEEDED","logs":{"edges":[{"cursor":"MQ","node":{"createdAt":"2020-12-21T11:53:14Z","payload":"Started uploading blueprint[id=a7be3c13-e138-4c43-a7c0-db50ef775613] on device[hardware_id=SIM-WTM]","severity":"INFO"}},{"cursor":"Mg","node":{"createdAt":"2020-12-21T11:53:14Z","payload":"Generating configuration for uploading","severity":"INFO"}},{"cursor":"Mw","node":{"createdAt":"2020-12-21T11:53:14Z","payload":"Updating configuration on the platform","severity":"INFO"}},{"cursor":"NA","node":{"createdAt":"2020-12-21T11:53:14Z","payload":"Uploading blueprint finished successfully","severity":"INFO"}}]}}}}} -{"data":{"device":{"blueprintUpdateOperation":{"status":"SUCCEEDED","logs":{"edges":[]}}}}} diff --git a/internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/settings.json b/internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/settings.json deleted file mode 100644 index e2bb1f5..0000000 --- a/internal/app/enaptercli/testdata/device_upload_logs/without_operation_id/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "hardware_id": "SIM-WTM" -} diff --git a/internal/app/enaptercli/testdata/helps/enapter b/internal/app/enaptercli/testdata/helps/enapter index b916872..2b8c36d 100644 --- a/internal/app/enaptercli/testdata/helps/enapter +++ b/internal/app/enaptercli/testdata/helps/enapter @@ -2,17 +2,16 @@ NAME: enaptercli.test - Command line interface for Enapter services. USAGE: - enaptercli.test [global options] command [command options] [arguments...] + enaptercli.test [global options] command [command options] DESCRIPTION: Enapter CLI requires access token for authentication. The token can be obtained in your Enapter Cloud account settings. - - Configure API token using ENAPTER_API_TOKEN environment variable or using --token global option. COMMANDS: - devices Device information and management commands. - rules Rules information and management commands. - help, h Shows a list of commands or help for one command + devices Manage devices + blueprints Manage blueprints + provisioning Create devices of different types + help, h Shows a list of commands or help for one command GLOBAL OPTIONS: - --help, -h show help (default: false) + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints b/internal/app/enaptercli/testdata/helps/enapter blueprints new file mode 100644 index 0000000..e1d96fd --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints @@ -0,0 +1,14 @@ +NAME: + enaptercli.test blueprints - Manage blueprints + +USAGE: + enaptercli.test blueprints command [command options] + +COMMANDS: + upload Upload blueprint directory into Platform + download Download blueprint zip from Platform + inspect Get blueprint metainfo + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints download b/internal/app/enaptercli/testdata/helps/enapter blueprints download new file mode 100644 index 0000000..4aa16c2 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints download @@ -0,0 +1,15 @@ +NAME: + enaptercli.test blueprints download - Download blueprint zip from Platform + +USAGE: + enaptercli.test blueprints download [command options] + +OPTIONS: + --blueprint_id value, -b value blueprint name or ID to download + --output value, -o value blueprint file name to save + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER_API_TOKEN Enapter API access token + ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints inspect b/internal/app/enaptercli/testdata/helps/enapter blueprints inspect new file mode 100644 index 0000000..4d6b277 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints inspect @@ -0,0 +1,13 @@ +NAME: + enaptercli.test blueprints inspect - Get blueprint metainfo + +USAGE: + enaptercli.test blueprints inspect [command options] + +OPTIONS: + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER_API_TOKEN Enapter API access token + ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints upload b/internal/app/enaptercli/testdata/helps/enapter blueprints upload new file mode 100644 index 0000000..f9c9ef5 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints upload @@ -0,0 +1,13 @@ +NAME: + enaptercli.test blueprints upload - Upload blueprint directory into Platform + +USAGE: + enaptercli.test blueprints upload [command options] + +OPTIONS: + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER_API_TOKEN Enapter API access token + ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter devices b/internal/app/enaptercli/testdata/helps/enapter devices index dddf861..56a381b 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices +++ b/internal/app/enaptercli/testdata/helps/enapter devices @@ -1,16 +1,14 @@ NAME: - enaptercli.test devices - Device information and management commands. + enaptercli.test devices - Manage devices USAGE: - enaptercli.test devices command [command options] [arguments...] + enaptercli.test devices command [command options] COMMANDS: - upload Upload blueprint to a device - logs Stream logs from a device - upload-logs Show blueprint uploading logs - execute Execute command on device - help, h Shows a list of commands or help for one command + assign_blueprint Assign blueprint to device + logs Show device logs + logsf Follow device logs + help, h Shows a list of commands or help for one command OPTIONS: - --help, -h show help (default: false) - + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint b/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint new file mode 100644 index 0000000..20c7f05 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint @@ -0,0 +1,15 @@ +NAME: + enaptercli.test devices assign_blueprint - Assign blueprint to device + +USAGE: + enaptercli.test devices assign_blueprint [command options] + +OPTIONS: + --device_id value, -d value device ID + --blueprint_id value, -b value blueprint ID to assign + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER_API_TOKEN Enapter API access token + ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter devices execute b/internal/app/enaptercli/testdata/helps/enapter devices execute deleted file mode 100644 index 7df8a65..0000000 --- a/internal/app/enaptercli/testdata/helps/enapter devices execute +++ /dev/null @@ -1,16 +0,0 @@ -NAME: - enaptercli.test devices execute - Execute command on device - -USAGE: - enaptercli.test devices execute [command options] [arguments...] - -OPTIONS: - --hardware-id value Hardware ID of the device; can be obtained in cloud.enapter.com - --command value Command name - --arguments value Command arguments as JSON object - --show-progress Enable in-progress responses streaming (default: false) - --help, -h show help (default: false) - -ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - diff --git a/internal/app/enaptercli/testdata/helps/enapter devices logs b/internal/app/enaptercli/testdata/helps/enapter devices logs index b68811b..e2ab624 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices logs +++ b/internal/app/enaptercli/testdata/helps/enapter devices logs @@ -1,13 +1,21 @@ NAME: - enaptercli.test devices logs - Stream logs from a device + enaptercli.test devices logs - Show device logs USAGE: - enaptercli.test devices logs [command options] [arguments...] + enaptercli.test devices logs [command options] OPTIONS: - --hardware-id value Hardware ID of the device; can be obtained in cloud.enapter.com - --help, -h show help (default: false) - + --device_id value, -d value device ID + --from value, -f value from timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z) + --to value, -t value to timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z) + --limit value, -l value maximum number of logs to retrieve (default: 0) + --offset value, -o value number of logs to skip on retrieve (default: 0) + --severity value, -s value filter logs by severity + --order value order logs by criteria (received_at_asc[default], received_at_desc) + --show value filter logs by criteria (all[default], persist_only, temporary_only) + --help, -h show help + ENVIRONMENT VARIABLES: ENAPTER_API_TOKEN Enapter API access token + ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter devices logsf b/internal/app/enaptercli/testdata/helps/enapter devices logsf new file mode 100644 index 0000000..7f0cb12 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter devices logsf @@ -0,0 +1,14 @@ +NAME: + enaptercli.test devices logsf - Follow device logs + +USAGE: + enaptercli.test devices logsf [command options] + +OPTIONS: + --device_id value, -d value device ID + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER_API_TOKEN Enapter API access token + ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter devices upload b/internal/app/enaptercli/testdata/helps/enapter devices upload deleted file mode 100644 index 89cfd31..0000000 --- a/internal/app/enaptercli/testdata/helps/enapter devices upload +++ /dev/null @@ -1,18 +0,0 @@ -NAME: - enaptercli.test devices upload - Upload blueprint to a device - -USAGE: - enaptercli.test devices upload [command options] [arguments...] - -DESCRIPTION: - Blueprint combines device capabilities declaration and Lua firmware for Enapter UCM. The command updates device blueprint and uploads the firmware to the UCM. Learn more about Enapter Blueprints at https://handbook.enapter.com/blueprints. - -OPTIONS: - --hardware-id value Hardware ID of the device; can be obtained in cloud.enapter.com - --timeout value Time to wait for blueprint uploading (default: 30s) - --blueprint-dir value Directory which contains blueprint file - --help, -h show help (default: false) - -ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - diff --git a/internal/app/enaptercli/testdata/helps/enapter devices upload-logs b/internal/app/enaptercli/testdata/helps/enapter devices upload-logs deleted file mode 100644 index fdd775d..0000000 --- a/internal/app/enaptercli/testdata/helps/enapter devices upload-logs +++ /dev/null @@ -1,15 +0,0 @@ -NAME: - enaptercli.test devices upload-logs - Show blueprint uploading logs - -USAGE: - enaptercli.test devices upload-logs [command options] [arguments...] - -OPTIONS: - --hardware-id value Hardware ID of the device; can be obtained in cloud.enapter.com - --timeout value Time to wait for blueprint uploading (default: 30s) - --operation-id value Uploading operation ID (optional) - --help, -h show help (default: false) - -ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning b/internal/app/enaptercli/testdata/helps/enapter provisioning new file mode 100644 index 0000000..c3fe2ef --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning @@ -0,0 +1,13 @@ +NAME: + enaptercli.test provisioning - Create devices of different types + +USAGE: + enaptercli.test provisioning command [command options] + +COMMANDS: + standalone Create a new standalone device + lua_device Create a new Lua device + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device b/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device new file mode 100644 index 0000000..bdc79ab --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device @@ -0,0 +1,16 @@ +NAME: + enaptercli.test provisioning lua_device - Create a new Lua device + +USAGE: + enaptercli.test provisioning lua_device [command options] + +OPTIONS: + --runtime_id value, -r value runtime UCM device ID where to run a new Lua device + --device_name value, -n value name of a new Lua device + --blueprint_id value, -b value blueprint ID of a new Lua device + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER_API_TOKEN Enapter API access token + ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone new file mode 100644 index 0000000..e070fd1 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone @@ -0,0 +1,15 @@ +NAME: + enaptercli.test provisioning standalone - Create a new standalone device + +USAGE: + enaptercli.test provisioning standalone [command options] + +OPTIONS: + --site_id value, -s value site ID where to craate device + --device_name value, -n value name for a new device + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER_API_TOKEN Enapter API access token + ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rules logs b/internal/app/enaptercli/testdata/helps/enapter rules logs deleted file mode 100644 index c831068..0000000 --- a/internal/app/enaptercli/testdata/helps/enapter rules logs +++ /dev/null @@ -1,13 +0,0 @@ -NAME: - enaptercli.test rules logs - Stream logs from a rule - -USAGE: - enaptercli.test rules logs [command options] [arguments...] - -OPTIONS: - --rule-id value Rule ID; can be obtained in cloud.enapter.com - --help, -h show help (default: false) - -ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - diff --git a/internal/app/enaptercli/testdata/helps/enapter rules update b/internal/app/enaptercli/testdata/helps/enapter rules update deleted file mode 100644 index f92a7af..0000000 --- a/internal/app/enaptercli/testdata/helps/enapter rules update +++ /dev/null @@ -1,17 +0,0 @@ -NAME: - enaptercli.test rules update - Update rule. - -USAGE: - enaptercli.test rules update [command options] [arguments...] - -OPTIONS: - --rule-id value Rule ID; can be obtained in cloud.enapter.com - --rule-path value Path to file with rule Lua code - --execution-interval value Rule execution interval in milliseconds (default: chosen by the server) - --stdlib-version value Version of standard library used by the rule (default: chosen by the server) - --timeout value Time to wait for rule update (default: 30s) - --help, -h show help (default: false) - -ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - diff --git a/internal/app/enaptercli/testdata/rules_logs/disconnect/invalid_token/input b/internal/app/enaptercli/testdata/rules_logs/disconnect/invalid_token/input deleted file mode 100644 index 37f6889..0000000 --- a/internal/app/enaptercli/testdata/rules_logs/disconnect/invalid_token/input +++ /dev/null @@ -1 +0,0 @@ -{"type":"disconnect","reason":"unauthorized","reconnect":false} diff --git a/internal/app/enaptercli/testdata/rules_logs/disconnect/invalid_token/output b/internal/app/enaptercli/testdata/rules_logs/disconnect/invalid_token/output deleted file mode 100644 index 1c44604..0000000 --- a/internal/app/enaptercli/testdata/rules_logs/disconnect/invalid_token/output +++ /dev/null @@ -1 +0,0 @@ -[connection] disconnected with reason: unauthorized diff --git a/internal/app/enaptercli/testdata/rules_logs/disconnect/rule_not_found/input b/internal/app/enaptercli/testdata/rules_logs/disconnect/rule_not_found/input deleted file mode 100644 index 3ba1f47..0000000 --- a/internal/app/enaptercli/testdata/rules_logs/disconnect/rule_not_found/input +++ /dev/null @@ -1,5 +0,0 @@ -{"type":"welcome"} - -{"identifier":"{\"channel\":\"RuleChannel\",\"rule_id\":\"SIM-RULE\"}","type":"message","message":{"topic":"error","payload":"Rule not found"}} - -{"identifier":"{\"channel\":\"RuleChannel\",\"rule_id\":\"SIM-RULE\"}","type":"reject_subscription"} diff --git a/internal/app/enaptercli/testdata/rules_logs/disconnect/rule_not_found/output b/internal/app/enaptercli/testdata/rules_logs/disconnect/rule_not_found/output deleted file mode 100644 index 41e8300..0000000 --- a/internal/app/enaptercli/testdata/rules_logs/disconnect/rule_not_found/output +++ /dev/null @@ -1,3 +0,0 @@ -[connection] welcome -[error] Rule not found -[connection] disconnected diff --git a/internal/app/enaptercli/testdata/rules_logs/simple/input b/internal/app/enaptercli/testdata/rules_logs/simple/input deleted file mode 100644 index b2317d9..0000000 --- a/internal/app/enaptercli/testdata/rules_logs/simple/input +++ /dev/null @@ -1,14 +0,0 @@ -{"type":"welcome"} -{"identifier":"{\"channel\":\"RuleChannel\",\"rule_id\":\"SIM-RULE\"}","type":"confirm_subscription"} - -{"type":"ping","message":"1606485743"} - -{"type":"message","identifier":"{\"channel\":\"RuleChannel\",\"rule_id\":\"SIM-OTHER\"}","message":{"topic":"info","payload":"{\"timestamp\":1606485747,\"status\":\"ok\"}"}} - -{"type":"ping","message":"1606485746"} - -{"type":"message","identifier":"{\"channel\":\"OtherChannel\",\"rule_id\":\"SIM-RULE\"}","message":{"topic":"info","payload":"{\"timestamp\":1606485747,\"status\":\"ok\"}"}} - -{"type":"ping","message":"1606485749"} - -{"type":"message","identifier":"{\"rule_id\":\"SIM-RULE\",\"channel\":\"RuleChannel\"}","message":{"topic":"info","payload":"{\"timestamp\":1606486092,\"status\":\"ok\"}"}} diff --git a/internal/app/enaptercli/testdata/rules_logs/simple/output b/internal/app/enaptercli/testdata/rules_logs/simple/output deleted file mode 100644 index d03a3b3..0000000 --- a/internal/app/enaptercli/testdata/rules_logs/simple/output +++ /dev/null @@ -1,5 +0,0 @@ -[connection] welcome -[connection] confirm_subscription -[read_error] skip message with unknown identifier map[channel:RuleChannel rule_id:SIM-OTHER] -[read_error] skip message with unknown identifier map[channel:OtherChannel rule_id:SIM-RULE] -[info] {"timestamp":1606486092,"status":"ok"} diff --git a/internal/app/enaptercli/testdata/rules_update/errors/output b/internal/app/enaptercli/testdata/rules_update/errors/output deleted file mode 100644 index e64ea7a..0000000 --- a/internal/app/enaptercli/testdata/rules_update/errors/output +++ /dev/null @@ -1,3 +0,0 @@ -[ERROR] hmm... wait a minute -[ERROR] oops! -app exit with error: request execution failed diff --git a/internal/app/enaptercli/testdata/rules_update/errors/requests b/internal/app/enaptercli/testdata/rules_update/errors/requests deleted file mode 100644 index 462e2c2..0000000 --- a/internal/app/enaptercli/testdata/rules_update/errors/requests +++ /dev/null @@ -1 +0,0 @@ -{"query":"mutation($input:UpdateInput!){rule{update(input: $input){data{code,message,title},errors{code,message,path,title}}}}","variables":{"input":{"ruleId":"SIM-RULE","luaCode":"-- Rule\n"}}} diff --git a/internal/app/enaptercli/testdata/rules_update/errors/responses b/internal/app/enaptercli/testdata/rules_update/errors/responses deleted file mode 100644 index 2167716..0000000 --- a/internal/app/enaptercli/testdata/rules_update/errors/responses +++ /dev/null @@ -1 +0,0 @@ -{"data":{"rule":{"update":{"data":null,"errors":[{"code":"warning","message":"hmm... wait a minute","title":"Started"},{"code":"fatal","message":"oops!","title":"Started"}]}}}} diff --git a/internal/app/enaptercli/testdata/rules_update/errors/rule.lua b/internal/app/enaptercli/testdata/rules_update/errors/rule.lua deleted file mode 100644 index 1be38ac..0000000 --- a/internal/app/enaptercli/testdata/rules_update/errors/rule.lua +++ /dev/null @@ -1 +0,0 @@ --- Rule diff --git a/internal/app/enaptercli/testdata/rules_update/errors/settings.json b/internal/app/enaptercli/testdata/rules_update/errors/settings.json deleted file mode 100644 index f429bfc..0000000 --- a/internal/app/enaptercli/testdata/rules_update/errors/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "rule_id": "SIM-RULE", - "rule_path": "rule.lua" -} diff --git a/internal/app/enaptercli/testdata/rules_update/simple/output b/internal/app/enaptercli/testdata/rules_update/simple/output deleted file mode 100644 index cdc1f76..0000000 --- a/internal/app/enaptercli/testdata/rules_update/simple/output +++ /dev/null @@ -1 +0,0 @@ -Rule successfully updated diff --git a/internal/app/enaptercli/testdata/rules_update/simple/requests b/internal/app/enaptercli/testdata/rules_update/simple/requests deleted file mode 100644 index 462e2c2..0000000 --- a/internal/app/enaptercli/testdata/rules_update/simple/requests +++ /dev/null @@ -1 +0,0 @@ -{"query":"mutation($input:UpdateInput!){rule{update(input: $input){data{code,message,title},errors{code,message,path,title}}}}","variables":{"input":{"ruleId":"SIM-RULE","luaCode":"-- Rule\n"}}} diff --git a/internal/app/enaptercli/testdata/rules_update/simple/responses b/internal/app/enaptercli/testdata/rules_update/simple/responses deleted file mode 100644 index 48b742f..0000000 --- a/internal/app/enaptercli/testdata/rules_update/simple/responses +++ /dev/null @@ -1 +0,0 @@ -{"data":{"rule":{"update":{"data":{"message":"Rule successfully updated"}}}}} diff --git a/internal/app/enaptercli/testdata/rules_update/simple/rule.lua b/internal/app/enaptercli/testdata/rules_update/simple/rule.lua deleted file mode 100644 index 1be38ac..0000000 --- a/internal/app/enaptercli/testdata/rules_update/simple/rule.lua +++ /dev/null @@ -1 +0,0 @@ --- Rule diff --git a/internal/app/enaptercli/testdata/rules_update/simple/settings.json b/internal/app/enaptercli/testdata/rules_update/simple/settings.json deleted file mode 100644 index f429bfc..0000000 --- a/internal/app/enaptercli/testdata/rules_update/simple/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "rule_id": "SIM-RULE", - "rule_path": "rule.lua" -} diff --git a/internal/cloudapi/client.go b/internal/cloudapi/client.go deleted file mode 100644 index b1c8a22..0000000 --- a/internal/cloudapi/client.go +++ /dev/null @@ -1,76 +0,0 @@ -package cloudapi - -import ( - "fmt" - "io" - "net/http" - - "github.com/shurcooL/graphql" -) - -type Client struct { - client *graphql.Client -} - -func NewClientWithURL(httpClient *http.Client, host string) *Client { - if httpClient == nil { - httpClient = http.DefaultClient - } - return &Client{ - client: graphql.NewClient(host, httpClient), - } -} - -type CredentialsTransport struct { - tripper http.RoundTripper - token string - version string -} - -func NewCredentialsTransport(t http.RoundTripper, token, version string) http.RoundTripper { - return CredentialsTransport{ - tripper: t, - token: token, - version: version, - } -} - -func (t CredentialsTransport) RoundTrip(r *http.Request) (*http.Response, error) { - newReq := new(http.Request) - *newReq = *r - - newReq.Header = make(http.Header, len(r.Header)) - for k, s := range r.Header { - newReq.Header[k] = s - } - - newReq.Header.Set("Authorization", "Bearer "+t.token) - newReq.Header.Set("X-ENAPTER-CLI-VERSION", t.version) - - return t.tripper.RoundTrip(newReq) -} - -type CLIMessageWriterTransport struct { - tripper http.RoundTripper - writer io.Writer -} - -func NewCLIMessageWriterTransport(t http.RoundTripper, w io.Writer) http.RoundTripper { - return CLIMessageWriterTransport{ - tripper: t, - writer: w, - } -} - -func (t CLIMessageWriterTransport) RoundTrip(r *http.Request) (*http.Response, error) { - resp, err := t.tripper.RoundTrip(r) - if err != nil { - return nil, err - } - - if msg := resp.Header.Get("X-ENAPTER-CLI-MESSAGE"); msg != "" { - fmt.Fprintln(t.writer, msg) - } - - return resp, nil -} diff --git a/internal/cloudapi/devices.go b/internal/cloudapi/devices.go deleted file mode 100644 index 0df313d..0000000 --- a/internal/cloudapi/devices.go +++ /dev/null @@ -1,172 +0,0 @@ -package cloudapi - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/shurcooL/graphql" -) - -type UploadBlueprintData struct { - Code string - Message string - Title string - OperationID string -} - -type UploadBlueprintError struct { - Code string - Message string - Path []string - Title string -} - -type uploadBlueprintMutation struct { - Device struct { - UploadBlueprint struct { - Data UploadBlueprintData - Errors []UploadBlueprintError - } `graphql:"uploadBlueprint(input: $input)"` - } -} - -func (c *Client) UploadBlueprint( - ctx context.Context, hardwareID string, blueprint []byte, -) (UploadBlueprintData, []UploadBlueprintError, error) { - type UploadBlueprintInput struct { - Blueprint graphql.String `json:"blueprint"` - HardwareID graphql.ID `json:"hardwareId"` - } - - variables := map[string]interface{}{ - "input": UploadBlueprintInput{ - Blueprint: graphql.String(blueprint), - HardwareID: graphql.String(hardwareID), - }, - } - - var mutation uploadBlueprintMutation - if err := c.client.Mutate(ctx, &mutation, variables); err != nil { - if errors.Is(err, context.DeadlineExceeded) { - err = ErrRequestTimedOut - } - return UploadBlueprintData{}, nil, fmt.Errorf("mutate: %w", err) - } - - uploadInfo := mutation.Device.UploadBlueprint - return uploadInfo.Data, uploadInfo.Errors, nil -} - -type OperationLog struct { - Payload string - CreatedAt string - Severity string -} - -type blueprintUpdateOperationQuery struct { - Device *struct { - BlueprintUpdateOperation *struct { - Status string - Logs struct { - Edges []struct { - Cursor graphql.String - Node OperationLog - } - } `graphql:"logs(after: $after_cursor)"` - } `graphql:"blueprintUpdateOperation(id: $operation_id)"` - } `graphql:"device(hardwareId: $hardware_id)"` -} - -func (c *Client) WriteOperationLogs( - ctx context.Context, hardwareID, operationID string, - writeLog func(operationID string, log OperationLog), -) error { - v := map[string]interface{}{ - "after_cursor": graphql.String(""), - "hardware_id": hardwareID, - "operation_id": operationID, - } - - for { - var q blueprintUpdateOperationQuery - if err := c.client.Query(ctx, &q, v); err != nil { - if errors.Is(err, context.DeadlineExceeded) { - err = ErrRequestTimedOut - } - return fmt.Errorf("failed to send request: %w", err) - } - - if q.Device == nil { - return fmt.Errorf("%w: device not found", ErrFinishedWithError) - } - - if q.Device.BlueprintUpdateOperation == nil { - return fmt.Errorf("%w: operation not found", ErrFinishedWithError) - } - - status := q.Device.BlueprintUpdateOperation.Status - logs := q.Device.BlueprintUpdateOperation.Logs - - for _, e := range logs.Edges { - v["after_cursor"] = e.Cursor - writeLog(operationID, e.Node) - } - - if len(logs.Edges) == 0 { - switch status { - case "SUCCEEDED": - return nil - case "ERROR": - return ErrLogStatusError - } - } - - const logRequestPeriod = 100 * time.Millisecond - time.Sleep(logRequestPeriod) - } -} - -type blueprintUpdateOperationsQuery struct { - Device *struct { - BlueprintUpdateOperations struct { - Nodes []struct { - ID string - } - } `graphql:"blueprintUpdateOperations(last: $last_int)"` - } `graphql:"device(hardwareId: $hardware_id)"` -} - -func (c *Client) WriteLastOperationsLogs( - ctx context.Context, hardwareID string, lastOperationsNumber int, - writeLog func(operationID string, log OperationLog), -) error { - v := map[string]interface{}{ - "hardware_id": hardwareID, - "last_int": graphql.Int(lastOperationsNumber), - } - - var q blueprintUpdateOperationsQuery - if err := c.client.Query(ctx, &q, v); err != nil { - if errors.Is(err, context.DeadlineExceeded) { - err = ErrRequestTimedOut - } - return fmt.Errorf("failed to send request: %w", err) - } - - if q.Device == nil { - return fmt.Errorf("%w: device not found", ErrFinishedWithError) - } - - for _, op := range q.Device.BlueprintUpdateOperations.Nodes { - if err := c.WriteOperationLogs(ctx, hardwareID, op.ID, writeLog); err != nil { - if errors.Is(err, ErrLogStatusError) { - continue - } - return err - } - } - - return nil -} diff --git a/internal/cloudapi/errors.go b/internal/cloudapi/errors.go deleted file mode 100644 index 559c7a6..0000000 --- a/internal/cloudapi/errors.go +++ /dev/null @@ -1,10 +0,0 @@ -package cloudapi - -import "errors" - -var ( - ErrFinishedWithError = errors.New("request execution failed") - ErrLogStatusError = errors.New("error during request execution") - ErrRequestTimedOut = errors.New("request timed out") - ErrFinished = errors.New("finished") -) diff --git a/internal/cloudapi/logs_writer.go b/internal/cloudapi/logs_writer.go deleted file mode 100644 index 4ae69ee..0000000 --- a/internal/cloudapi/logs_writer.go +++ /dev/null @@ -1,272 +0,0 @@ -package cloudapi - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "net/url" - "sync" - - "github.com/gorilla/websocket" -) - -const ( - fieldChannel = "channel" - fieldHardwareID = "hardware_id" - fieldRuleID = "rule_id" -) - -type LogsWriter struct { - url string - identifier map[string]string - writeLog func(topic, message string) - wsConnMu sync.Mutex - wsConn *websocket.Conn -} - -func NewDeviceLogsWriter( - host, token, apiVersion, hardwareID string, - writer func(topic, message string), -) (*LogsWriter, error) { - identifier := map[string]string{ - fieldChannel: "DeviceChannel", - fieldHardwareID: hardwareID, - } - return newLogsWriter(host, token, apiVersion, identifier, writer) -} - -func NewRuleLogsWriter( - host, token, apiVersion, ruleID string, - writer func(topic, message string), -) (*LogsWriter, error) { - identifier := map[string]string{ - fieldChannel: "RuleChannel", - fieldRuleID: ruleID, - } - return newLogsWriter(host, token, apiVersion, identifier, writer) -} - -func newLogsWriter( - host, token, apiVersion string, identifier map[string]string, - writer func(topic, message string), -) (*LogsWriter, error) { - u, err := url.Parse(host) - if err != nil { - return nil, fmt.Errorf("parse url: %w", err) - } - - q := u.Query() - q.Set("token", token) - q.Set("enapter_api_version", apiVersion) - u.RawQuery = q.Encode() - - return &LogsWriter{ - url: u.String(), - identifier: identifier, - writeLog: writer, - }, nil -} - -func (l *LogsWriter) Run(ctx context.Context) error { - defer l.close() - - go func() { - <-ctx.Done() - l.close() - }() - - for { - select { - case <-ctx.Done(): - return nil - default: - } - - if err := l.connect(ctx); err != nil { - l.writeLog("connection", fmt.Sprintf("failed to connect: %s", err.Error())) - continue - } - - if err := l.subscribe(); err != nil { - l.writeLog("connection", fmt.Sprintf("failed to subscribe: %s", err.Error())) - continue - } - - if err := l.readAndWriteLogs(ctx); err != nil { - if errors.Is(err, ErrFinished) { - return nil - } - l.writeLog("read_error", fmt.Sprintf("failed to read msg: %s", err.Error())) - return err - } - } -} - -func (l *LogsWriter) connect(ctx context.Context) error { - wsConn, resp, err := websocket.DefaultDialer.DialContext(ctx, l.url, nil) - if err != nil { - return fmt.Errorf("websockets dial: %w", err) - } - defer resp.Body.Close() - - l.wsConnMu.Lock() - defer l.wsConnMu.Unlock() - l.wsConn = wsConn - - return nil -} - -func (l *LogsWriter) close() { - l.wsConnMu.Lock() - defer l.wsConnMu.Unlock() - - if l.wsConn != nil { - l.wsConn.Close() - } -} - -func (l *LogsWriter) subscribe() error { - identifierBytes, err := json.Marshal(l.identifier) - if err != nil { - return fmt.Errorf("failed to marshal sm: %w", err) - } - - msg := struct { - Command string `json:"command"` - Identifier string `json:"identifier"` - }{ - Command: "subscribe", - Identifier: string(identifierBytes), - } - - msgBytes, err := json.Marshal(&msg) - if err != nil { - return fmt.Errorf("failed to marshal m: %w", err) - } - - err = l.wsConn.WriteMessage(websocket.TextMessage, msgBytes) - if err != nil { - return fmt.Errorf("failed to subscribe: %w", err) - } - - return nil -} - -func (l *LogsWriter) readAndWriteLogs(ctx context.Context) error { - for { - msgType, msgBytes, err := l.wsConn.ReadMessage() - select { - case <-ctx.Done(): - return nil - default: - } - - if err != nil { - return err - } - - if msgType != websocket.TextMessage { - l.writeLog("read_error", - fmt.Sprintf("skip unsupported message type [%d] (only text type [%d] supported)", - msgType, websocket.TextMessage)) - continue - } - - if err := l.process(msgBytes); err != nil { - return err - } - } -} - -type logsBaseMessage struct { - Type string `json:"type"` - Identifier string `json:"identifier"` - Message json.RawMessage `json:"message"` -} - -type logsMessage struct { - Topic string `json:"topic"` - Payload string `json:"payload"` -} - -type disconnectMessage struct { - Type string `json:"type"` - Reason string `json:"reason"` - Reconnect bool `json:"reconnect"` -} - -func (l *LogsWriter) process(msgBytes []byte) error { - baseMsg := logsBaseMessage{} - if err := json.Unmarshal(msgBytes, &baseMsg); err != nil { - errMsg := fmt.Sprintf("skip invalid message %s: %s", string(msgBytes), err.Error()) - l.writeLog("read_error", errMsg) - return err - } - - switch baseMsg.Type { - case "ping": - case "welcome", "confirm_subscription": - l.writeLog("connection", baseMsg.Type) - case "reject_subscription": - l.writeLog("connection", "disconnected") - return ErrFinished - case "disconnect": - return l.processDisconnect(msgBytes) - case "message": - l.processMessage(baseMsg) - default: - l.writeLog("unknown", string(msgBytes)) - } - - return nil -} - -func (l *LogsWriter) processDisconnect(msgBytes []byte) error { - var msg disconnectMessage - if err := json.Unmarshal(msgBytes, &msg); err != nil { - return nil - } - if msg.Reconnect { - l.writeLog("connection", - fmt.Sprintf("disconnected with reason: %s. Reconnecting...", msg.Reason)) - return nil - } - l.writeLog("connection", - fmt.Sprintf("disconnected with reason: %s", msg.Reason)) - return ErrFinished -} - -func (l *LogsWriter) processMessage(baseMsg logsBaseMessage) { - var identifier map[string]string - if err := json.Unmarshal([]byte(baseMsg.Identifier), &identifier); err != nil { - l.writeLog("read_error", - fmt.Sprintf("skip message with invalid identifier %s: %s", baseMsg.Identifier, err.Error())) - return - } - if !mapsEqual(l.identifier, identifier) { - l.writeLog("read_error", - fmt.Sprintf("skip message with unknown identifier %+v", identifier)) - return - } - - var msg logsMessage - if err := json.Unmarshal(baseMsg.Message, &msg); err != nil { - l.writeLog("read_error", - fmt.Sprintf("skip invalid log message %s: %s", string(baseMsg.Message), err.Error())) - return - } - l.writeLog(msg.Topic, msg.Payload) -} - -func mapsEqual(m1, m2 map[string]string) bool { - if len(m1) != len(m2) { - return false - } - for k, v1 := range m1 { - if v2, ok := m2[k]; !ok || v1 != v2 { - return false - } - } - return true -} diff --git a/internal/cloudapi/rules.go b/internal/cloudapi/rules.go deleted file mode 100644 index bf1b42e..0000000 --- a/internal/cloudapi/rules.go +++ /dev/null @@ -1,67 +0,0 @@ -package cloudapi - -import ( - "context" - "errors" - "fmt" - - "github.com/shurcooL/graphql" -) - -type UpdateRuleInput struct { - RuleID string - LuaCode string - StdlibVersion string - ExecutionInterval int -} - -type UpdateRuleData struct { - Code string - Message string - Title string -} - -type UpdateRuleError struct { - Code string - Message string - Path []string - Title string -} - -func (c *Client) UpdateRule( - ctx context.Context, input UpdateRuleInput, -) (UpdateRuleData, []UpdateRuleError, error) { - var mutation struct { - Rule struct { - Update struct { - Data UpdateRuleData - Errors []UpdateRuleError - } `graphql:"update(input: $input)"` - } - } - - type UpdateInput struct { - RuleID graphql.String `json:"ruleId"` - LuaCode graphql.String `json:"luaCode"` - StdlibVersion graphql.String `json:"stdlibVersion,omitempty"` - ExecutionInterval graphql.Int `json:"executionInterval,omitempty"` - } - - variables := map[string]interface{}{ - "input": UpdateInput{ - RuleID: graphql.String(input.RuleID), - LuaCode: graphql.String(input.LuaCode), - StdlibVersion: graphql.String(input.StdlibVersion), - ExecutionInterval: graphql.Int(input.ExecutionInterval), - }, - } - - if err := c.client.Mutate(ctx, &mutation, variables); err != nil { - if errors.Is(err, context.DeadlineExceeded) { - err = ErrRequestTimedOut - } - return UpdateRuleData{}, nil, fmt.Errorf("mutate: %w", err) - } - - return mutation.Rule.Update.Data, mutation.Rule.Update.Errors, nil -} diff --git a/internal/publichttp/client.go b/internal/publichttp/client.go deleted file mode 100644 index effa227..0000000 --- a/internal/publichttp/client.go +++ /dev/null @@ -1,105 +0,0 @@ -package publichttp - -import ( - "context" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "strconv" - "time" -) - -const defaultBaseURL = "https://api.enapter.com" - -type Client struct { - baseURL *url.URL - client *http.Client - - // Services used for talking to different parts of the Enapter API. - Commands CommandsAPI -} - -func NewClient(httpClient *http.Client) *Client { - c, err := NewClientWithURL(httpClient, defaultBaseURL) - if err != nil { - panic(err) - } - return c -} - -func NewClientWithURL(httpClient *http.Client, baseURL string) (*Client, error) { - if httpClient == nil { - httpClient = http.DefaultClient - } - - u, err := url.Parse(baseURL) - if err != nil { - return nil, err - } - - c := &Client{baseURL: u, client: httpClient} - c.Commands = CommandsAPI{client: c} - return c, nil -} - -func (c *Client) NewRequest(method, path string, body io.Reader) (*http.Request, error) { - return c.NewRequestWithContext(context.Background(), method, path, body) -} - -func (c *Client) NewRequestWithContext( - ctx context.Context, method, path string, body io.Reader, -) (*http.Request, error) { - u, err := c.baseURL.Parse(path) - if err != nil { - return nil, fmt.Errorf("parse url: %w", err) - } - - req, err := http.NewRequestWithContext(ctx, method, u.String(), body) - if err != nil { - return nil, fmt.Errorf("create request: %w", err) - } - - return req, err -} - -func (c *Client) Do(req *http.Request) (*http.Response, error) { - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - - if resp.StatusCode >= http.StatusMultipleChoices { - defer resp.Body.Close() - - responseError, err := c.parseResponseError(resp) - if err != nil { - return nil, err - } - return nil, responseError - } - - return resp, nil -} - -func (c *Client) parseResponseError(r *http.Response) (ResponseError, error) { - var errors ResponseError - - if r.Body != http.NoBody { - if err := json.NewDecoder(r.Body).Decode(&errors); err != nil { - return ResponseError{}, fmt.Errorf("unmarshal body: %w", err) - } - } - - errors.StatusCode = r.StatusCode - if retryAfter := r.Header.Get("Retry-After"); retryAfter != "" { - duration, err := strconv.Atoi(retryAfter) - if err != nil { - return ResponseError{}, fmt.Errorf("parse Retry-After: %w", err) - } - errors.RetryAfter = time.Duration(duration) * time.Second - } - - return errors, nil -} diff --git a/internal/publichttp/commands.go b/internal/publichttp/commands.go deleted file mode 100644 index 24eae76..0000000 --- a/internal/publichttp/commands.go +++ /dev/null @@ -1,114 +0,0 @@ -package publichttp - -import ( - "bufio" - "bytes" - "context" - "encoding/json" - "fmt" - "net/http" - "strconv" -) - -type CommandsAPI struct { - client *Client -} - -type CommandQuery struct { - HardwareID string `json:"hardware_id"` - CommandName string `json:"command_name"` - Arguments map[string]interface{} `json:"arguments"` -} - -type CommandResponse struct { - State CommandState `json:"state"` - Payload map[string]interface{} `json:"payload,omitempty"` -} - -type CommandState string - -const ( - CommandSucceeded CommandState = "succeeded" - CommandError CommandState = "error" - CommandPlatformError CommandState = "platform_error" - CommandStarted CommandState = "started" - CommandInProgress CommandState = "device_in_progress" -) - -func (c *CommandsAPI) Execute( - ctx context.Context, query CommandQuery, -) (CommandResponse, error) { - resp, err := c.execute(ctx, query, false) - if err != nil { - return CommandResponse{}, err - } - defer resp.Body.Close() - - var cmdResp CommandResponse - if err := json.NewDecoder(resp.Body).Decode(&cmdResp); err != nil { - return CommandResponse{}, fmt.Errorf("unmarshal response: %w", err) - } - - return cmdResp, nil -} - -type CommandProgress struct { - CommandResponse - Error error -} - -func (c *CommandsAPI) ExecuteWithProgress( - ctx context.Context, query CommandQuery, -) (<-chan CommandProgress, error) { - //nolint:bodyclose // closed in the reading goroutine - resp, err := c.execute(ctx, query, true) - if err != nil { - return nil, err - } - - progressCh := make(chan CommandProgress) - go func() { - defer resp.Body.Close() - defer close(progressCh) - - scanner := bufio.NewScanner(resp.Body) - for scanner.Scan() { - var p CommandProgress - p.Error = json.Unmarshal(scanner.Bytes(), &p.CommandResponse) - - select { - case <-ctx.Done(): - return - case progressCh <- p: - } - } - }() - - return progressCh, nil -} - -func (c *CommandsAPI) execute( - ctx context.Context, query CommandQuery, showProgress bool, -) (*http.Response, error) { - queryBody := new(bytes.Buffer) - if err := json.NewEncoder(queryBody).Encode(query); err != nil { - return nil, fmt.Errorf("marshal body: %w", err) - } - - const path = "/commands/v1/execute" - req, err := c.client.NewRequestWithContext(ctx, http.MethodPost, path, queryBody) - if err != nil { - return nil, fmt.Errorf("create request: %w", err) - } - - values := req.URL.Query() - values.Set("show_progress", strconv.FormatBool(showProgress)) - req.URL.RawQuery = values.Encode() - - resp, err := c.client.Do(req) - if err != nil { - return nil, err - } - - return resp, nil -} diff --git a/internal/publichttp/errors.go b/internal/publichttp/errors.go deleted file mode 100644 index 297992b..0000000 --- a/internal/publichttp/errors.go +++ /dev/null @@ -1,30 +0,0 @@ -package publichttp - -import ( - "fmt" - "strings" - "time" -) - -type ResponseError struct { - Errors []Error `json:"errors"` - StatusCode int `json:"-"` - RetryAfter time.Duration `json:"-"` -} - -func (r ResponseError) Error() string { - var builder strings.Builder - for i, e := range r.Errors { - if i > 0 { - builder.WriteByte('\n') - } - builder.WriteString(fmt.Sprintf("%v: %v", e.Code, e.Message)) - } - return builder.String() -} - -type Error struct { - Code string `json:"code"` - Message string `json:"message"` - Details map[string]interface{} `json:"details,omitempty"` -} diff --git a/internal/publichttp/transport.go b/internal/publichttp/transport.go deleted file mode 100644 index d5892fd..0000000 --- a/internal/publichttp/transport.go +++ /dev/null @@ -1,28 +0,0 @@ -package publichttp - -import "net/http" - -type AuthTokenTransport struct { - token string - next http.RoundTripper -} - -func NewAuthTokenTransport(t http.RoundTripper, token string) http.RoundTripper { - return &AuthTokenTransport{token: token, next: t} -} - -func (t *AuthTokenTransport) RoundTrip(req *http.Request) (*http.Response, error) { - const header = "X-Enapter-Auth-Token" - s := cloneRequest(req) - s.Header.Set(header, t.token) - return t.next.RoundTrip(s) -} - -func cloneRequest(req *http.Request) *http.Request { - shallow := new(http.Request) - *shallow = *req - for k, s := range req.Header { - shallow.Header[k] = s - } - return shallow -} From d8016f58ae7d2730915846523b79190781847ea5 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Tue, 5 Nov 2024 18:33:54 +0400 Subject: [PATCH 02/88] fix: use v3 as API prefix --- internal/app/enaptercli/cmd_base.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 97cce1c..9cc4e4c 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -73,7 +73,7 @@ type doHTTPRequestParams struct { } func (c *cmdBase) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { - req, err := http.NewRequestWithContext(ctx, p.Method, c.apiHost+"/v2"+p.Path, p.Body) + req, err := http.NewRequestWithContext(ctx, p.Method, c.apiHost+"/v3"+p.Path, p.Body) if err != nil { return fmt.Errorf("build http request: %w", err) } From 5ab985f2e529d562c9327106939b8bdbc6dea247 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Wed, 6 Nov 2024 14:02:11 +0400 Subject: [PATCH 03/88] fix: invalid path for devices commands --- internal/app/enaptercli/cmd_devices.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/app/enaptercli/cmd_devices.go b/internal/app/enaptercli/cmd_devices.go index 56d88eb..7b31d72 100644 --- a/internal/app/enaptercli/cmd_devices.go +++ b/internal/app/enaptercli/cmd_devices.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "io" + "net/url" "github.com/urfave/cli/v2" ) @@ -38,7 +39,11 @@ func (c *cmdDevices) Flags() []cli.Flag { } func (c *cmdDevices) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { - p.Path = "/devices/" + c.deviceID + path, err := url.JoinPath("/devices", c.deviceID, p.Path) + if err != nil { + return fmt.Errorf("join path: %w", err) + } + p.Path = path return c.cmdBase.doHTTPRequest(ctx, p) } From b456abc1214bbfffc90f3a9e29d254be8b1493d5 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 11 Nov 2024 15:00:58 +0300 Subject: [PATCH 04/88] chore: rename v3 version binary to enapter3 --- .gitignore | 4 ++-- .goreleaser.yml | 6 +++--- README.md | 6 ++---- build.sh | 2 +- internal/app/enaptercli/cmd_base.go | 8 ++++---- internal/app/enaptercli/errors.go | 2 +- .../enaptercli/testdata/helps/enapter blueprints download | 4 ++-- .../enaptercli/testdata/helps/enapter blueprints inspect | 4 ++-- .../enaptercli/testdata/helps/enapter blueprints upload | 4 ++-- .../testdata/helps/enapter devices assign_blueprint | 4 ++-- .../app/enaptercli/testdata/helps/enapter devices logs | 4 ++-- .../app/enaptercli/testdata/helps/enapter devices logsf | 4 ++-- .../testdata/helps/enapter provisioning lua_device | 4 ++-- .../testdata/helps/enapter provisioning standalone | 4 ++-- 14 files changed, 29 insertions(+), 31 deletions(-) diff --git a/.gitignore b/.gitignore index c8ac242..f81949e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ .vscode -^enapter$ +^enapter3$ dist -.DS_Store \ No newline at end of file +.DS_Store diff --git a/.goreleaser.yml b/.goreleaser.yml index f87ff78..3706b10 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -3,7 +3,7 @@ version: 2 project_name: enapter-cli builds: - - binary: enapter + - binary: enapter3 main: ./cmd/enapter/ ldflags: -s -w -X main.version={{.Version}} -X main.commit={{.ShortCommit}} -X main.date={{.Date}} env: @@ -54,6 +54,6 @@ brews: description: Command-line tool for Enapter Energy Management System Toolkit install: | - bin.install "enapter" + bin.install "enapter3" test: | - assert_match "Enapter CLI #{version}", shell_output("#{bin}/enapter --version") + assert_match "Enapter CLI #{version}", shell_output("#{bin}/enapter3 --version") diff --git a/README.md b/README.md index 1f93b79..a32a695 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,6 @@ This tool helps Enapter customers to work with devices. It useful in the followi brew tap enapter/tap && brew install enapter@3 ``` -If you already have installed previous version you probably need to update symblink as brew suggested you after install new version. - ### Get prebuilt binaries Choose your platform and required release on the [Releases page](https://github.com/Enapter/enapter-cli/releases). @@ -46,10 +44,10 @@ Enapter CLI requires access token for authentication. Obtaining of the token is 4. Follow the instructions on the screen -5. Set environment variable `ENAPTER_API_TOKEN` with new token. To make it permanent don't forget to add it to configuration files of your shell. +5. Set environment variable `ENAPTER3_API_TOKEN` with new token. To make it permanent don't forget to add it to configuration files of your shell. ```bash - export ENAPTER_API_TOKEN="your token" + export ENAPTER3_API_TOKEN="your token" ``` Please note that if you don't save your token, it is not possible to reveal it anymore. You need generate new token. diff --git a/build.sh b/build.sh index 4b7a413..74fadde 100755 --- a/build.sh +++ b/build.sh @@ -2,7 +2,7 @@ set -ex -output=${1:-enapter} +output=${1:-enapter3} BUILD_VERSION=$(git describe --tag 2> /dev/null) BUILD_COMMIT=$(git rev-parse --short HEAD) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 9cc4e4c..9a9b9c7 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -25,7 +25,7 @@ func (c *cmdBase) Flags() []cli.Flag { &cli.StringFlag{ Name: "token", Usage: "Enapter API token", - EnvVars: []string{"ENAPTER_API_TOKEN"}, + EnvVars: []string{"ENAPTER3_API_TOKEN"}, Hidden: true, Destination: &c.token, Category: "HTTP API Configuration:", @@ -33,7 +33,7 @@ func (c *cmdBase) Flags() []cli.Flag { &cli.StringFlag{ Name: "api-host", Usage: "Override API endpoint", - EnvVars: []string{"ENAPTER_API_HOST"}, + EnvVars: []string{"ENAPTER3_API_HOST"}, Hidden: true, Value: "https://api.enapter.com", Destination: &c.apiHost, @@ -58,8 +58,8 @@ func (c *cmdBase) Before(cliCtx *cli.Context) error { func (c *cmdBase) HelpTemplate() string { return cli.CommandHelpTemplate + ` ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) ` } diff --git a/internal/app/enaptercli/errors.go b/internal/app/enaptercli/errors.go index 947727b..2c2ddf9 100644 --- a/internal/app/enaptercli/errors.go +++ b/internal/app/enaptercli/errors.go @@ -4,7 +4,7 @@ import "errors" var ( errAPITokenMissed = errors.New("API token missing. Set it up using environment " + - "variable ENAPTER_API_TOKEN") + "variable ENAPTER3_API_TOKEN") errBlueprintIDMissed = errors.New("blueprint ID is missed") errBlueprintPathMissed = errors.New("blueprint path is missed") errUnsupportedFlagValue = errors.New("unsupported flag value") diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints download b/internal/app/enaptercli/testdata/helps/enapter blueprints download index 4aa16c2..e04599c 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints download @@ -10,6 +10,6 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints inspect b/internal/app/enaptercli/testdata/helps/enapter blueprints inspect index 4d6b277..51a96cb 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints inspect +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints inspect @@ -8,6 +8,6 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints upload b/internal/app/enaptercli/testdata/helps/enapter blueprints upload index f9c9ef5..dc5dbbd 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints upload @@ -8,6 +8,6 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint b/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint index 20c7f05..5b55a88 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint @@ -10,6 +10,6 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter devices logs b/internal/app/enaptercli/testdata/helps/enapter devices logs index e2ab624..e44208a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices logs +++ b/internal/app/enaptercli/testdata/helps/enapter devices logs @@ -16,6 +16,6 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter devices logsf b/internal/app/enaptercli/testdata/helps/enapter devices logsf index 7f0cb12..847caed 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices logsf +++ b/internal/app/enaptercli/testdata/helps/enapter devices logsf @@ -9,6 +9,6 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device b/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device index bdc79ab..97330b8 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device @@ -11,6 +11,6 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone index e070fd1..126e57a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone @@ -10,6 +10,6 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER_API_TOKEN Enapter API access token - ENAPTER_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) From 108fe597d8fd3df3b555f7512a58cd928f9ee402 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 12 Nov 2024 15:30:44 +0300 Subject: [PATCH 05/88] chore: change command keys format (from _ to -) --- internal/app/enaptercli/cmd_blueprints_download.go | 2 +- internal/app/enaptercli/cmd_devices.go | 2 +- internal/app/enaptercli/cmd_devices_assign_blueprint.go | 2 +- internal/app/enaptercli/cmd_provisioning_lua_device.go | 6 +++--- internal/app/enaptercli/cmd_provisioning_standalone.go | 4 ++-- .../enaptercli/testdata/helps/enapter blueprints download | 2 +- .../testdata/helps/enapter devices assign_blueprint | 4 ++-- internal/app/enaptercli/testdata/helps/enapter devices logs | 2 +- .../app/enaptercli/testdata/helps/enapter devices logsf | 2 +- .../testdata/helps/enapter provisioning lua_device | 6 +++--- .../testdata/helps/enapter provisioning standalone | 4 ++-- 11 files changed, 18 insertions(+), 18 deletions(-) diff --git a/internal/app/enaptercli/cmd_blueprints_download.go b/internal/app/enaptercli/cmd_blueprints_download.go index 54ec5ca..4c5a135 100644 --- a/internal/app/enaptercli/cmd_blueprints_download.go +++ b/internal/app/enaptercli/cmd_blueprints_download.go @@ -35,7 +35,7 @@ func buildCmdBlueprintsDownload() *cli.Command { func (c *cmdBlueprintsDownload) Flags() []cli.Flag { flags := c.cmdBlueprints.Flags() return append(flags, &cli.StringFlag{ - Name: "blueprint_id", + Name: "blueprint-id", Aliases: []string{"b"}, Usage: "blueprint name or ID to download", Destination: &c.blueprintID, diff --git a/internal/app/enaptercli/cmd_devices.go b/internal/app/enaptercli/cmd_devices.go index 7b31d72..c17627c 100644 --- a/internal/app/enaptercli/cmd_devices.go +++ b/internal/app/enaptercli/cmd_devices.go @@ -30,7 +30,7 @@ func buildCmdDevices() *cli.Command { func (c *cmdDevices) Flags() []cli.Flag { flags := c.cmdBase.Flags() return append(flags, &cli.StringFlag{ - Name: "device_id", + Name: "device-id", Aliases: []string{"d"}, Usage: "device ID", Destination: &c.deviceID, diff --git a/internal/app/enaptercli/cmd_devices_assign_blueprint.go b/internal/app/enaptercli/cmd_devices_assign_blueprint.go index 39bc2c9..f455119 100644 --- a/internal/app/enaptercli/cmd_devices_assign_blueprint.go +++ b/internal/app/enaptercli/cmd_devices_assign_blueprint.go @@ -32,7 +32,7 @@ func buildCmdDevicesAssignBlueprint() *cli.Command { func (c *cmdDevicesAssignBlueprint) Flags() []cli.Flag { flags := c.cmdDevices.Flags() return append(flags, &cli.StringFlag{ - Name: "blueprint_id", + Name: "blueprint-id", Aliases: []string{"b"}, Usage: "blueprint ID to assign", Destination: &c.blueprintID, diff --git a/internal/app/enaptercli/cmd_provisioning_lua_device.go b/internal/app/enaptercli/cmd_provisioning_lua_device.go index 3421240..6764a87 100644 --- a/internal/app/enaptercli/cmd_provisioning_lua_device.go +++ b/internal/app/enaptercli/cmd_provisioning_lua_device.go @@ -34,19 +34,19 @@ func buildCmdProvisioningLua() *cli.Command { func (c *cmdProvisioningLua) Flags() []cli.Flag { flags := c.cmdProvisioning.Flags() return append(flags, &cli.StringFlag{ - Name: "runtime_id", + Name: "runtime-id", Aliases: []string{"r"}, Usage: "runtime UCM device ID where to run a new Lua device", Destination: &c.runtimeID, Required: true, }, &cli.StringFlag{ - Name: "device_name", + Name: "device-name", Aliases: []string{"n"}, Usage: "name of a new Lua device", Destination: &c.deviceName, Required: true, }, &cli.StringFlag{ - Name: "blueprint_id", + Name: "blueprint-id", Aliases: []string{"b"}, Usage: "blueprint ID of a new Lua device", Destination: &c.blueprintID, diff --git a/internal/app/enaptercli/cmd_provisioning_standalone.go b/internal/app/enaptercli/cmd_provisioning_standalone.go index 7c1fef2..d141160 100644 --- a/internal/app/enaptercli/cmd_provisioning_standalone.go +++ b/internal/app/enaptercli/cmd_provisioning_standalone.go @@ -33,13 +33,13 @@ func buildCmdProvisioningStandalone() *cli.Command { func (c *cmdProvisioningStandalone) Flags() []cli.Flag { flags := c.cmdProvisioning.Flags() return append(flags, &cli.StringFlag{ - Name: "site_id", + Name: "site-id", Aliases: []string{"s"}, Usage: "site ID where to craate device", Destination: &c.siteID, Required: true, }, &cli.StringFlag{ - Name: "device_name", + Name: "device-name", Aliases: []string{"n"}, Usage: "name for a new device", Destination: &c.deviceName, diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints download b/internal/app/enaptercli/testdata/helps/enapter blueprints download index e04599c..ed2810b 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints download @@ -5,7 +5,7 @@ USAGE: enaptercli.test blueprints download [command options] OPTIONS: - --blueprint_id value, -b value blueprint name or ID to download + --blueprint-id value, -b value blueprint name or ID to download --output value, -o value blueprint file name to save --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint b/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint index 5b55a88..1ee8c1a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint @@ -5,8 +5,8 @@ USAGE: enaptercli.test devices assign_blueprint [command options] OPTIONS: - --device_id value, -d value device ID - --blueprint_id value, -b value blueprint ID to assign + --device-id value, -d value device ID + --blueprint-id value, -b value blueprint ID to assign --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter devices logs b/internal/app/enaptercli/testdata/helps/enapter devices logs index e44208a..8437511 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices logs +++ b/internal/app/enaptercli/testdata/helps/enapter devices logs @@ -5,7 +5,7 @@ USAGE: enaptercli.test devices logs [command options] OPTIONS: - --device_id value, -d value device ID + --device-id value, -d value device ID --from value, -f value from timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z) --to value, -t value to timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z) --limit value, -l value maximum number of logs to retrieve (default: 0) diff --git a/internal/app/enaptercli/testdata/helps/enapter devices logsf b/internal/app/enaptercli/testdata/helps/enapter devices logsf index 847caed..9bbdc8b 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices logsf +++ b/internal/app/enaptercli/testdata/helps/enapter devices logsf @@ -5,7 +5,7 @@ USAGE: enaptercli.test devices logsf [command options] OPTIONS: - --device_id value, -d value device ID + --device-id value, -d value device ID --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device b/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device index 97330b8..2b326c9 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device @@ -5,9 +5,9 @@ USAGE: enaptercli.test provisioning lua_device [command options] OPTIONS: - --runtime_id value, -r value runtime UCM device ID where to run a new Lua device - --device_name value, -n value name of a new Lua device - --blueprint_id value, -b value blueprint ID of a new Lua device + --runtime-id value, -r value runtime UCM device ID where to run a new Lua device + --device-name value, -n value name of a new Lua device + --blueprint-id value, -b value blueprint ID of a new Lua device --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone index 126e57a..b723498 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone @@ -5,8 +5,8 @@ USAGE: enaptercli.test provisioning standalone [command options] OPTIONS: - --site_id value, -s value site ID where to craate device - --device_name value, -n value name for a new device + --site-id value, -s value site ID where to craate device + --device-name value, -n value name for a new device --help, -h show help ENVIRONMENT VARIABLES: From 225cd0ce75b02e1594d471190263bea28d534194 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 10 Dec 2024 15:18:28 +0300 Subject: [PATCH 06/88] feat: add profiles download/upload commands --- internal/app/enaptercli/cmd_blueprints.go | 1 + .../app/enaptercli/cmd_blueprints_profiles.go | 20 ++++++ .../cmd_blueprints_profiles_download.go | 62 +++++++++++++++++++ .../cmd_blueprints_profiles_upload.go | 55 ++++++++++++++++ internal/app/enaptercli/errors.go | 1 + .../testdata/helps/enapter blueprints | 1 + .../enapter blueprints profiles download | 14 +++++ .../helps/enapter blueprints profiles upload | 13 ++++ 8 files changed, 167 insertions(+) create mode 100644 internal/app/enaptercli/cmd_blueprints_profiles.go create mode 100644 internal/app/enaptercli/cmd_blueprints_profiles_download.go create mode 100644 internal/app/enaptercli/cmd_blueprints_profiles_upload.go create mode 100644 internal/app/enaptercli/testdata/helps/enapter blueprints profiles download create mode 100644 internal/app/enaptercli/testdata/helps/enapter blueprints profiles upload diff --git a/internal/app/enaptercli/cmd_blueprints.go b/internal/app/enaptercli/cmd_blueprints.go index 03bd9f6..77a7aad 100644 --- a/internal/app/enaptercli/cmd_blueprints.go +++ b/internal/app/enaptercli/cmd_blueprints.go @@ -15,6 +15,7 @@ func buildCmdBlueprints() *cli.Command { Name: "blueprints", Usage: "Manage blueprints", Subcommands: []*cli.Command{ + buildCmdBlueprintsProfiles(), buildCmdBlueprintsUpload(), buildCmdBlueprintsDownload(), buildCmdBlueprintsInspect(), diff --git a/internal/app/enaptercli/cmd_blueprints_profiles.go b/internal/app/enaptercli/cmd_blueprints_profiles.go new file mode 100644 index 0000000..73b47c8 --- /dev/null +++ b/internal/app/enaptercli/cmd_blueprints_profiles.go @@ -0,0 +1,20 @@ +package enaptercli + +import ( + "github.com/urfave/cli/v2" +) + +type cmdBlueprintsProfiles struct { + cmdBase +} + +func buildCmdBlueprintsProfiles() *cli.Command { + return &cli.Command{ + Name: "profiles", + Usage: "Manage blueprints profiles", + Subcommands: []*cli.Command{ + buildCmdBlueprintsProfilesDownload(), + buildCmdBlueprintsProfilesUpload(), + }, + } +} diff --git a/internal/app/enaptercli/cmd_blueprints_profiles_download.go b/internal/app/enaptercli/cmd_blueprints_profiles_download.go new file mode 100644 index 0000000..bfb8b83 --- /dev/null +++ b/internal/app/enaptercli/cmd_blueprints_profiles_download.go @@ -0,0 +1,62 @@ +package enaptercli + +import ( + "context" + "fmt" + "io" + "net/http" + "os" + + "github.com/urfave/cli/v2" +) + +type cmdBlueprintsProfilesDownload struct { + cmdBlueprintsProfiles + outputFileName string +} + +func buildCmdBlueprintsProfilesDownload() *cli.Command { + cmd := &cmdBlueprintsProfilesDownload{} + return &cli.Command{ + Name: "download", + Usage: "Download profiles zip from Platform", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdBlueprintsProfilesDownload) Flags() []cli.Flag { + flags := c.cmdBlueprintsProfiles.Flags() + return append(flags, &cli.StringFlag{ + Name: "output", + Aliases: []string{"o"}, + Usage: "file name to save", + Destination: &c.outputFileName, + }) +} + +func (c *cmdBlueprintsProfilesDownload) do(ctx context.Context) error { + if c.outputFileName == "" { + c.outputFileName = "profiles.zip" + } + + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/blueprints/download_device_profiles", + //nolint:bodyclose //body is closed in doHTTPRequest + RespProcessor: okRespBodyProcessor(func(body io.Reader) error { + outFile, err := os.Create(c.outputFileName) + if err != nil { + return fmt.Errorf("create output file %q: %w", c.outputFileName, err) + } + if _, err := io.Copy(outFile, body); err != nil { + return fmt.Errorf("write output file %q: %w", c.outputFileName, err) + } + return nil + }), + }) +} diff --git a/internal/app/enaptercli/cmd_blueprints_profiles_upload.go b/internal/app/enaptercli/cmd_blueprints_profiles_upload.go new file mode 100644 index 0000000..07f5985 --- /dev/null +++ b/internal/app/enaptercli/cmd_blueprints_profiles_upload.go @@ -0,0 +1,55 @@ +package enaptercli + +import ( + "bytes" + "context" + "fmt" + "net/http" + "os" + + "github.com/urfave/cli/v2" +) + +type cmdBlueprintsProfilesUpload struct { + cmdBlueprintsProfiles +} + +func buildCmdBlueprintsProfilesUpload() *cli.Command { + cmd := &cmdBlueprintsProfilesUpload{} + return &cli.Command{ + Name: "upload", + Usage: "Upload profiles into Platform", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Args: true, + ArgsUsage: "profiles zip file path", + Action: func(cliCtx *cli.Context) error { + return cmd.upload(cliCtx.Context, cliCtx.Args().Get(0)) + }, + } +} + +func (c *cmdBlueprintsProfilesUpload) Before(cliCtx *cli.Context) error { + if err := c.cmdBlueprintsProfiles.Before(cliCtx); err != nil { + return err + } + + if cliCtx.Args().Get(0) == "" { + return errProfilesPathMissed + } + return nil +} + +func (c *cmdBlueprintsProfilesUpload) upload(ctx context.Context, blueprintPath string) error { + data, err := os.ReadFile(blueprintPath) + if err != nil { + return fmt.Errorf("read zip file: %w", err) + } + + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/blueprints/upload_device_profiles", + Body: bytes.NewReader(data), + }) +} diff --git a/internal/app/enaptercli/errors.go b/internal/app/enaptercli/errors.go index 2c2ddf9..63f31be 100644 --- a/internal/app/enaptercli/errors.go +++ b/internal/app/enaptercli/errors.go @@ -8,4 +8,5 @@ var ( errBlueprintIDMissed = errors.New("blueprint ID is missed") errBlueprintPathMissed = errors.New("blueprint path is missed") errUnsupportedFlagValue = errors.New("unsupported flag value") + errProfilesPathMissed = errors.New("profiles path is missed") ) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints b/internal/app/enaptercli/testdata/helps/enapter blueprints index e1d96fd..a7b2073 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints @@ -5,6 +5,7 @@ USAGE: enaptercli.test blueprints command [command options] COMMANDS: + profiles Manage blueprints profiles upload Upload blueprint directory into Platform download Download blueprint zip from Platform inspect Get blueprint metainfo diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints profiles download b/internal/app/enaptercli/testdata/helps/enapter blueprints profiles download new file mode 100644 index 0000000..c03a06e --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints profiles download @@ -0,0 +1,14 @@ +NAME: + enaptercli.test blueprints profiles download - Download profiles zip from Platform + +USAGE: + enaptercli.test blueprints profiles download [command options] + +OPTIONS: + --output value, -o value file name to save + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints profiles upload b/internal/app/enaptercli/testdata/helps/enapter blueprints profiles upload new file mode 100644 index 0000000..ed2d1e5 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints profiles upload @@ -0,0 +1,13 @@ +NAME: + enaptercli.test blueprints profiles upload - Upload profiles into Platform + +USAGE: + enaptercli.test blueprints profiles upload [command options] profiles zip file path + +OPTIONS: + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + From 76c48cf66d10b3939ae500b48bdaef718aabc4be Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 10 Dec 2024 17:51:30 +0300 Subject: [PATCH 07/88] refactor,tests: don't compare helps in update case --- internal/app/enaptercli/execute_test.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/internal/app/enaptercli/execute_test.go b/internal/app/enaptercli/execute_test.go index 0feda1e..b81ab86 100644 --- a/internal/app/enaptercli/execute_test.go +++ b/internal/app/enaptercli/execute_test.go @@ -33,12 +33,21 @@ func TestHelpMessages(t *testing.T) { if update { err := os.WriteFile(exepctedFileName, actual, 0o600) require.NoError(t, err) + } else { + require.Equal(t, readFileToString(t, exepctedFileName), string(actual)) } - - expected, err := os.ReadFile(exepctedFileName) - require.NoError(t, err) - - require.Equal(t, string(expected), string(actual)) }) } } + +func readFileToString(t *testing.T, path string) string { + t.Helper() + return string(shouldReadFile(t, path)) +} + +func shouldReadFile(t *testing.T, path string) []byte { + t.Helper() + d, err := os.ReadFile(path) + require.NoError(t, err) + return d +} From d677015a4f229c59c88b81c06b7cada2ba4096df Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 10 Dec 2024 17:51:56 +0300 Subject: [PATCH 08/88] tests: add http req tests --- internal/app/enaptercli/execute_test.go | 98 +++++++++++++++++++ .../blueprints_inspect_by_id/cmd.tmpl | 1 + .../blueprints_inspect_by_id/out | 1 + .../blueprints_inspect_by_id/req_0 | 16 +++ .../blueprints_inspect_by_id/resp_0 | 1 + .../blueprints_inspect_by_name/cmd.tmpl | 1 + .../blueprints_inspect_by_name/out | 1 + .../blueprints_inspect_by_name/req_0 | 16 +++ .../blueprints_inspect_by_name/resp_0 | 1 + .../devices_assign_blueprint/cmd.tmpl | 1 + .../devices_assign_blueprint/out | 1 + .../devices_assign_blueprint/req_0 | 19 ++++ .../devices_assign_blueprint/resp_0 | 1 + 13 files changed, 158 insertions(+) create mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/resp_0 diff --git a/internal/app/enaptercli/execute_test.go b/internal/app/enaptercli/execute_test.go index b81ab86..1b9995c 100644 --- a/internal/app/enaptercli/execute_test.go +++ b/internal/app/enaptercli/execute_test.go @@ -1,15 +1,23 @@ package enaptercli_test import ( + "bytes" + "encoding/json" "io" + "net/http" + "net/http/httptest" "os" "path/filepath" + "strconv" "strings" "testing" + "text/template" "github.com/stretchr/testify/require" ) +const testToken = "enapter_api_test_token" + func TestHelpMessages(t *testing.T) { files, err := os.ReadDir("testdata/helps") require.NoError(t, err) @@ -40,6 +48,82 @@ func TestHelpMessages(t *testing.T) { } } +func TestHTTPReqResp(t *testing.T) { + const testdataPath = "testdata/http_req_resp" + tests, err := os.ReadDir(testdataPath) + require.NoError(t, err) + + for _, tc := range tests { + tc := tc + t.Run(tc.Name(), func(t *testing.T) { + reqCount := 0 + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + reqObj := struct { + Method string + URL string + Header http.Header + Body string + }{ + r.Method, + r.URL.String(), + r.Header, + readBodyAsString(t, r.Body), + } + expReqFileName := filepath.Join(testdataPath, tc.Name(), "req_"+strconv.Itoa(reqCount)) + if update { + err := os.WriteFile(expReqFileName, shouldMarshalIndent(t, reqObj), 0o600) + require.NoError(t, err) + } + + resp := shouldReadFile(t, filepath.Join(testdataPath, tc.Name(), "resp_"+strconv.Itoa(reqCount))) + _, _ = w.Write(resp) + })) + defer srv.Close() + + tmplParams := struct { + BaseFlags string + }{ + BaseFlags: strings.Join([]string{"--token", testToken, "--api-host", srv.URL}, " "), + } + + cmd := executeTmpl(t, filepath.Join(testdataPath, tc.Name(), "cmd.tmpl"), tmplParams) + args := strings.Split(cmd, " ") + app := startTestApp(args...) + appErr := app.Wait() + + actual, err := io.ReadAll(app.Stdout()) + require.NoError(t, err) + + if appErr != nil { + actual = append(actual, []byte("app exit with error: "+appErr.Error()+"\n")...) + } + + exepctedOutFileName := filepath.Join(testdataPath, tc.Name(), "out") + if update { + err := os.WriteFile(exepctedOutFileName, actual, 0o600) + require.NoError(t, err) + } else { + require.Equal(t, readFileToString(t, exepctedOutFileName), string(actual)) + } + }) + } +} + +func executeTmpl(t *testing.T, tmplFilePath string, tmplParams interface{}) string { + t.Helper() + tmplData := readFileToString(t, tmplFilePath) + tmplData = strings.TrimRight(tmplData, " \n\t") + + tmpl := template.New(tmplFilePath) + tmpl, err := tmpl.Parse(tmplData) + require.NoError(t, err) + + out := &bytes.Buffer{} + require.NoError(t, tmpl.Execute(out, tmplParams)) + + return out.String() +} + func readFileToString(t *testing.T, path string) string { t.Helper() return string(shouldReadFile(t, path)) @@ -51,3 +135,17 @@ func shouldReadFile(t *testing.T, path string) []byte { require.NoError(t, err) return d } + +func readBodyAsString(t *testing.T, r io.Reader) string { + t.Helper() + d, err := io.ReadAll(r) + require.NoError(t, err) + return string(d) +} + +func shouldMarshalIndent(t *testing.T, v interface{}) []byte { + t.Helper() + d, err := json.MarshalIndent(v, "", " ") + require.NoError(t, err) + return d +} diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/cmd.tmpl new file mode 100644 index 0000000..83e0a90 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/cmd.tmpl @@ -0,0 +1 @@ +enapter3 blueprints inspect {{.BaseFlags}} cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/out b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/out new file mode 100644 index 0000000..fca5dda --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/out @@ -0,0 +1 @@ +{"created_at": TODAY!} diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/req_0 new file mode 100644 index 0000000..8a06bd6 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/req_0 @@ -0,0 +1,16 @@ +{ + "Method": "GET", + "URL": "/v3/blueprints/cdd82438-dda8-4f69-aad1-0be9adeab964", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/resp_0 new file mode 100644 index 0000000..fca5dda --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/resp_0 @@ -0,0 +1 @@ +{"created_at": TODAY!} diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/cmd.tmpl new file mode 100644 index 0000000..12ab423 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/cmd.tmpl @@ -0,0 +1 @@ +enapter3 blueprints inspect {{.BaseFlags}} test_blueprint_name diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/out b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/out new file mode 100644 index 0000000..fca5dda --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/out @@ -0,0 +1 @@ +{"created_at": TODAY!} diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/req_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/req_0 new file mode 100644 index 0000000..5ee90c8 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/req_0 @@ -0,0 +1,16 @@ +{ + "Method": "GET", + "URL": "/v3/blueprints/enapter/test_blueprint_name/latest", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/resp_0 new file mode 100644 index 0000000..fca5dda --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/resp_0 @@ -0,0 +1 @@ +{"created_at": TODAY!} diff --git a/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/cmd.tmpl new file mode 100644 index 0000000..7e640cb --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/cmd.tmpl @@ -0,0 +1 @@ +enapter3 devices assign_blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/out b/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/out new file mode 100644 index 0000000..d832236 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/out @@ -0,0 +1 @@ +{"blueprint": "assigned"} diff --git a/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/req_0 b/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/req_0 new file mode 100644 index 0000000..e8eeabb --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "POST", + "URL": "/v3/devices/427ec09e-ec1e-4760-acc1-50106533b875/assign_blueprint", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Length": [ + "55" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "{\"blueprint_id\":\"cdd82438-dda8-4f69-aad1-0be9adeab964\"}" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/resp_0 new file mode 100644 index 0000000..d832236 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/resp_0 @@ -0,0 +1 @@ +{"blueprint": "assigned"} From 13a7520a88afdffc6ddaaf608762b48149e443b2 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 16 Dec 2024 11:44:43 +0300 Subject: [PATCH 09/88] feat: name for blueprint archive as *.enbp --- internal/app/enaptercli/cmd_blueprints_download.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/app/enaptercli/cmd_blueprints_download.go b/internal/app/enaptercli/cmd_blueprints_download.go index 4c5a135..8310753 100644 --- a/internal/app/enaptercli/cmd_blueprints_download.go +++ b/internal/app/enaptercli/cmd_blueprints_download.go @@ -51,7 +51,7 @@ func (c *cmdBlueprintsDownload) Flags() []cli.Flag { func (c *cmdBlueprintsDownload) do(ctx context.Context) error { if c.outputFileName == "" { c.outputFileName = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(c.blueprintID, - ":", "_"), ".", "_"), "/", "_") + ".zip" + ":", "_"), ".", "_"), "/", "_") + ".enbp" } if !isBlueprintID(c.blueprintID) { From 089b1a3f8eedf7ee458a304bb950f36080e1c878 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 24 Dec 2024 18:53:52 +0300 Subject: [PATCH 10/88] feat: support rule-engine commands --- .gitignore | 3 +- .golangci.yml | 2 +- internal/app/enaptercli/cmd_rule_engine.go | 35 ++++++ .../app/enaptercli/cmd_rule_engine_inspect.go | 33 ++++++ .../app/enaptercli/cmd_rule_engine_resume.go | 33 ++++++ .../app/enaptercli/cmd_rule_engine_rule.go | 44 +++++++ .../enaptercli/cmd_rule_engine_rule_create.go | 108 ++++++++++++++++++ .../enaptercli/cmd_rule_engine_rule_delete.go | 57 +++++++++ .../cmd_rule_engine_rule_disable.go | 59 ++++++++++ .../enaptercli/cmd_rule_engine_rule_enable.go | 57 +++++++++ .../cmd_rule_engine_rule_inspect.go | 45 ++++++++ .../enaptercli/cmd_rule_engine_rule_list.go | 33 ++++++ .../enaptercli/cmd_rule_engine_rule_update.go | 85 ++++++++++++++ .../cmd_rule_engine_rule_update_script.go | 91 +++++++++++++++ .../app/enaptercli/cmd_rule_engine_suspend.go | 33 ++++++ internal/app/enaptercli/execute.go | 1 + .../app/enaptercli/testdata/helps/enapter | 1 + .../testdata/helps/enapter rule-engine | 15 +++ .../helps/enapter rule-engine inspect | 13 +++ .../testdata/helps/enapter rule-engine resume | 13 +++ .../testdata/helps/enapter rule-engine rule | 19 +++ .../helps/enapter rule-engine rule create | 19 +++ .../helps/enapter rule-engine rule delete | 14 +++ .../helps/enapter rule-engine rule disable | 14 +++ .../helps/enapter rule-engine rule enable | 14 +++ .../helps/enapter rule-engine rule inspect | 14 +++ .../helps/enapter rule-engine rule list | 13 +++ .../helps/enapter rule-engine rule update | 16 +++ .../enapter rule-engine rule update-script | 17 +++ .../helps/enapter rule-engine suspend | 13 +++ 30 files changed, 912 insertions(+), 2 deletions(-) create mode 100644 internal/app/enaptercli/cmd_rule_engine.go create mode 100644 internal/app/enaptercli/cmd_rule_engine_inspect.go create mode 100644 internal/app/enaptercli/cmd_rule_engine_resume.go create mode 100644 internal/app/enaptercli/cmd_rule_engine_rule.go create mode 100644 internal/app/enaptercli/cmd_rule_engine_rule_create.go create mode 100644 internal/app/enaptercli/cmd_rule_engine_rule_delete.go create mode 100644 internal/app/enaptercli/cmd_rule_engine_rule_disable.go create mode 100644 internal/app/enaptercli/cmd_rule_engine_rule_enable.go create mode 100644 internal/app/enaptercli/cmd_rule_engine_rule_inspect.go create mode 100644 internal/app/enaptercli/cmd_rule_engine_rule_list.go create mode 100644 internal/app/enaptercli/cmd_rule_engine_rule_update.go create mode 100644 internal/app/enaptercli/cmd_rule_engine_rule_update_script.go create mode 100644 internal/app/enaptercli/cmd_rule_engine_suspend.go create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine inspect create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine resume create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine rule create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine rule create create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine rule inspect create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine rule list create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine rule update create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine suspend diff --git a/.gitignore b/.gitignore index f81949e..69518d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .vscode -^enapter3$ +enapter3 dist .DS_Store +.env diff --git a/.golangci.yml b/.golangci.yml index e93fa58..4cddfe8 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,7 +9,7 @@ linters: # - deadcode # - depguard - dogsled - - dupl + # - dupl # some commands are looks very similar in implementation - errcheck - errorlint - exhaustive diff --git a/internal/app/enaptercli/cmd_rule_engine.go b/internal/app/enaptercli/cmd_rule_engine.go new file mode 100644 index 0000000..f611e08 --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine.go @@ -0,0 +1,35 @@ +package enaptercli + +import ( + "context" + "fmt" + "net/url" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngine struct { + cmdBase +} + +func buildCmdRuleEngine() *cli.Command { + return &cli.Command{ + Name: "rule-engine", + Usage: "Manage the rule engine", + Subcommands: []*cli.Command{ + buildCmdRuleEngineInspect(), + buildCmdRuleEngineSuspend(), + buildCmdRuleEngineResume(), + buildCmdRuleEngineRule(), + }, + } +} + +func (c *cmdRuleEngine) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { + path, err := url.JoinPath("/site/rule_engine", p.Path) + if err != nil { + return fmt.Errorf("join path: %w", err) + } + p.Path = path + return c.cmdBase.doHTTPRequest(ctx, p) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_inspect.go b/internal/app/enaptercli/cmd_rule_engine_inspect.go new file mode 100644 index 0000000..304153c --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_inspect.go @@ -0,0 +1,33 @@ +package enaptercli + +import ( + "context" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngineInspect struct { + cmdRuleEngine +} + +func buildCmdRuleEngineInspect() *cli.Command { + cmd := &cmdRuleEngineInspect{} + return &cli.Command{ + Name: "inspect", + Usage: "Inspect the rule engine", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdRuleEngineInspect) do(ctx context.Context) error { + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "", + }) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_resume.go b/internal/app/enaptercli/cmd_rule_engine_resume.go new file mode 100644 index 0000000..c1a5b9f --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_resume.go @@ -0,0 +1,33 @@ +package enaptercli + +import ( + "context" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngineResume struct { + cmdRuleEngine +} + +func buildCmdRuleEngineResume() *cli.Command { + cmd := &cmdRuleEngineResume{} + return &cli.Command{ + Name: "resume", + Usage: "Resume execution of rules", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdRuleEngineResume) do(ctx context.Context) error { + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/resume", + }) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_rule.go b/internal/app/enaptercli/cmd_rule_engine_rule.go new file mode 100644 index 0000000..9fb536e --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_rule.go @@ -0,0 +1,44 @@ +package enaptercli + +import ( + "context" + "fmt" + "net/url" + + "github.com/urfave/cli/v2" +) + +const ( + ruleRuntimeVersion1 = 1 + ruleRuntimeVersion3 = 3 +) + +type cmdRuleEngineRule struct { + cmdRuleEngine +} + +func buildCmdRuleEngineRule() *cli.Command { + return &cli.Command{ + Name: "rule", + Usage: "Manage rules", + Subcommands: []*cli.Command{ + buildCmdRuleEngineRuleCreate(), + buildCmdRuleEngineRuleDelete(), + buildCmdRuleEngineRuleDisable(), + buildCmdRuleEngineRuleEnable(), + buildCmdRuleEngineRuleInspect(), + buildCmdRuleEngineRuleList(), + buildCmdRuleEngineRuleUpdate(), + buildCmdRuleEngineRuleUpdateScript(), + }, + } +} + +func (c *cmdRuleEngineRule) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { + path, err := url.JoinPath("/rules", p.Path) + if err != nil { + return fmt.Errorf("join path: %w", err) + } + p.Path = path + return c.cmdRuleEngine.doHTTPRequest(ctx, p) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_create.go b/internal/app/enaptercli/cmd_rule_engine_rule_create.go new file mode 100644 index 0000000..1603bb6 --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_rule_create.go @@ -0,0 +1,108 @@ +package enaptercli + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "os" + "time" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngineRuleCreate struct { + cmdRuleEngineRule + slug string + name string + scriptPath string + runtimeVersion int + execInterval time.Duration + disable bool +} + +func buildCmdRuleEngineRuleCreate() *cli.Command { + cmd := &cmdRuleEngineRuleCreate{} + return &cli.Command{ + Name: "create", + Usage: "Create a new rule", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdRuleEngineRuleCreate) Flags() []cli.Flag { + return append(c.cmdRuleEngineRule.Flags(), + &cli.StringFlag{ + Name: "slug", + Usage: "Slug of a new rule", + Destination: &c.slug, + Required: true, + }, + &cli.StringFlag{ + Name: "name", + Usage: "Name of a new rule", + Destination: &c.name, + }, + &cli.StringFlag{ + Name: "script", + Usage: "Path to a file containing the script code", + Destination: &c.scriptPath, + Required: true, + }, + &cli.IntFlag{ + Name: "runtime-version", + Usage: "Version of a runtime to use for the script execution", + Destination: &c.runtimeVersion, + Value: ruleRuntimeVersion3, + }, + &cli.DurationFlag{ + Name: "exec-interval", + Usage: "How often to execute the script. This option is only compatible with the runtime version 1", + Destination: &c.execInterval, + }, + &cli.BoolFlag{ + Name: "disable", + Usage: "Whether to disable a rule upon creation", + Destination: &c.disable, + }, + ) +} + +func (c *cmdRuleEngineRuleCreate) do(ctx context.Context) error { + if c.scriptPath == "-" { + c.scriptPath = "/dev/stdin" + } + scriptBytes, err := os.ReadFile(c.scriptPath) + if err != nil { + return fmt.Errorf("read script code file: %w", err) + } + + body, err := json.Marshal(map[string]any{ + "rule": map[string]any{ + "slug": c.slug, + "name": c.name, + "script": map[string]any{ + "code": base64.StdEncoding.EncodeToString(scriptBytes), + "runtime_version": c.runtimeVersion, + "exec_interval": c.execInterval.String(), + }, + }, + "disable_rule": c.disable, + }) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "", + Body: bytes.NewReader(body), + }) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_delete.go b/internal/app/enaptercli/cmd_rule_engine_rule_delete.go new file mode 100644 index 0000000..87de852 --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_rule_delete.go @@ -0,0 +1,57 @@ +package enaptercli + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngineRuleDelete struct { + cmdRuleEngineRule + ruleIDs []string +} + +func buildCmdRuleEngineRuleDelete() *cli.Command { + cmd := &cmdRuleEngineRuleDelete{} + return &cli.Command{ + Name: "delete", + Usage: "Delete one or more rules", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdRuleEngineRuleDelete) Flags() []cli.Flag { + return append(c.cmdRuleEngineRule.Flags(), + &cli.MultiStringFlag{ + Target: &cli.StringSliceFlag{ + Name: "rule-id", + Usage: "Rule IDs or slugs", + Required: true, + }, + Destination: &c.ruleIDs, + }, + ) +} + +func (c *cmdRuleEngineRuleDelete) do(ctx context.Context) error { + body, err := json.Marshal(map[string]any{ + "rule_ids": c.ruleIDs, + }) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/batch_delete", + Body: bytes.NewReader(body), + }) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_disable.go b/internal/app/enaptercli/cmd_rule_engine_rule_disable.go new file mode 100644 index 0000000..03711bd --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_rule_disable.go @@ -0,0 +1,59 @@ +package enaptercli + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngineRuleDisable struct { + cmdRuleEngineRule + ruleIDs []string +} + +func buildCmdRuleEngineRuleDisable() *cli.Command { + cmd := &cmdRuleEngineRuleDisable{} + return &cli.Command{ + Name: "disable", + Usage: "Disable one or more rules", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdRuleEngineRuleDisable) Flags() []cli.Flag { + return append(c.cmdRuleEngineRule.Flags(), + &cli.MultiStringFlag{ + Target: &cli.StringSliceFlag{ + Name: "rule-id", + Usage: "Rule IDs or slugs", + Required: true, + }, + Destination: &c.ruleIDs, + }, + ) +} + +func (c *cmdRuleEngineRuleDisable) do(ctx context.Context) error { + body, err := json.Marshal(map[string]any{ + "rule_ids": c.ruleIDs, + }) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + + fmt.Println(c.ruleIDs) + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/batch_disable", + Body: bytes.NewReader(body), + }) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_enable.go b/internal/app/enaptercli/cmd_rule_engine_rule_enable.go new file mode 100644 index 0000000..18cbaa9 --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_rule_enable.go @@ -0,0 +1,57 @@ +package enaptercli + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngineRuleEnable struct { + cmdRuleEngineRule + ruleIDs []string +} + +func buildCmdRuleEngineRuleEnable() *cli.Command { + cmd := &cmdRuleEngineRuleEnable{} + return &cli.Command{ + Name: "enable", + Usage: "Enable one or more rules", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdRuleEngineRuleEnable) Flags() []cli.Flag { + return append(c.cmdRuleEngineRule.Flags(), + &cli.MultiStringFlag{ + Target: &cli.StringSliceFlag{ + Name: "rule-id", + Usage: "Rule IDs or slugs", + Required: true, + }, + Destination: &c.ruleIDs, + }, + ) +} + +func (c *cmdRuleEngineRuleEnable) do(ctx context.Context) error { + body, err := json.Marshal(map[string]any{ + "rule_ids": c.ruleIDs, + }) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/batch_enable", + Body: bytes.NewReader(body), + }) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_inspect.go b/internal/app/enaptercli/cmd_rule_engine_rule_inspect.go new file mode 100644 index 0000000..91a0527 --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_rule_inspect.go @@ -0,0 +1,45 @@ +package enaptercli + +import ( + "context" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngineRuleInspect struct { + cmdRuleEngineRule + ruleID string +} + +func buildCmdRuleEngineRuleInspect() *cli.Command { + cmd := &cmdRuleEngineRuleInspect{} + return &cli.Command{ + Name: "inspect", + Usage: "Inspect a rule", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdRuleEngineRuleInspect) Flags() []cli.Flag { + return append(c.cmdRuleEngineRule.Flags(), + &cli.StringFlag{ + Name: "rule-id", + Usage: "Rule ID or slug", + Destination: &c.ruleID, + Required: true, + }, + ) +} + +func (c *cmdRuleEngineRuleInspect) do(ctx context.Context) error { + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/" + c.ruleID, + }) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_list.go b/internal/app/enaptercli/cmd_rule_engine_rule_list.go new file mode 100644 index 0000000..90aa6a8 --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_rule_list.go @@ -0,0 +1,33 @@ +package enaptercli + +import ( + "context" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngineRuleList struct { + cmdRuleEngineRule +} + +func buildCmdRuleEngineRuleList() *cli.Command { + cmd := &cmdRuleEngineRuleList{} + return &cli.Command{ + Name: "list", + Usage: "List rules", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdRuleEngineRuleList) do(ctx context.Context) error { + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "", + }) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update.go b/internal/app/enaptercli/cmd_rule_engine_rule_update.go new file mode 100644 index 0000000..d006e38 --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update.go @@ -0,0 +1,85 @@ +package enaptercli + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "strings" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngineRuleUpdate struct { + cmdRuleEngineRule + ruleID string + slug string + name string +} + +func buildCmdRuleEngineRuleUpdate() *cli.Command { + cmd := &cmdRuleEngineRuleUpdate{} + return &cli.Command{ + Name: "update", + Usage: "Update a rule", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx) + }, + } +} + +func (c *cmdRuleEngineRuleUpdate) Flags() []cli.Flag { + return append(c.cmdRuleEngineRule.Flags(), + &cli.StringFlag{ + Name: "rule-id", + Usage: "Rule ID or slug to update", + Destination: &c.ruleID, + Required: true, + }, + &cli.StringFlag{ + Name: "slug", + Usage: "A new rule slug", + Destination: &c.slug, + }, + &cli.StringFlag{ + Name: "name", + Usage: "A new rule name", + Destination: &c.name, + }, + ) +} + +func (c *cmdRuleEngineRuleUpdate) do(cliCtx *cli.Context) error { + payload := struct { + Rule map[string]any `json:"rule"` + UpdateMask string `json:"update_mask"` + }{ + Rule: make(map[string]any), + UpdateMask: "", + } + + if cliCtx.IsSet("slug") { + payload.Rule["slug"] = c.slug + payload.UpdateMask += "slug," + } + if cliCtx.IsSet("name") { + payload.Rule["name"] = c.name + payload.UpdateMask += "name," + } + + payload.UpdateMask = strings.TrimSuffix(payload.UpdateMask, ",") + + body, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + + return c.doHTTPRequest(cliCtx.Context, doHTTPRequestParams{ + Method: http.MethodPatch, + Path: "/" + c.ruleID, + Body: bytes.NewReader(body), + }) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go new file mode 100644 index 0000000..0dfd6ba --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go @@ -0,0 +1,91 @@ +package enaptercli + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "os" + "time" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngineRuleUpdateScript struct { + cmdRuleEngineRule + ruleID string + scriptPath string + runtimeVersion int + execInterval time.Duration +} + +func buildCmdRuleEngineRuleUpdateScript() *cli.Command { + cmd := &cmdRuleEngineRuleUpdateScript{} + return &cli.Command{ + Name: "update-script", + Usage: "Update the script of a rule", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdRuleEngineRuleUpdateScript) Flags() []cli.Flag { + return append(c.cmdRuleEngineRule.Flags(), + &cli.StringFlag{ + Name: "rule-id", + Usage: "Rule ID or slug to update", + Destination: &c.ruleID, + Required: true, + }, + &cli.StringFlag{ + Name: "script", + Usage: "Path to a file containing the script code", + Destination: &c.scriptPath, + Required: true, + }, + &cli.IntFlag{ + Name: "runtime-version", + Usage: "Version of a runtime to use for the script execution", + Destination: &c.runtimeVersion, + Value: ruleRuntimeVersion3, + }, + &cli.DurationFlag{ + Name: "exec-interval", + Usage: "How often to execute the script. This option is only compatible with the runtime version 1", + Destination: &c.execInterval, + }, + ) +} + +func (c *cmdRuleEngineRuleUpdateScript) do(ctx context.Context) error { + if c.scriptPath == "-" { + c.scriptPath = "/dev/stdin" + } + scriptBytes, err := os.ReadFile(c.scriptPath) + if err != nil { + return fmt.Errorf("read script file: %w", err) + } + + body, err := json.Marshal(map[string]any{ + "new_rule_script": map[string]any{ + "code": base64.StdEncoding.EncodeToString(scriptBytes), + "runtime_version": c.runtimeVersion, + "exec_interval": c.execInterval.String(), + }, + }) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/" + c.ruleID + "/update_script", + Body: bytes.NewReader(body), + }) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_suspend.go b/internal/app/enaptercli/cmd_rule_engine_suspend.go new file mode 100644 index 0000000..fa0f5cb --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_suspend.go @@ -0,0 +1,33 @@ +package enaptercli + +import ( + "context" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngineSuspend struct { + cmdRuleEngine +} + +func buildCmdRuleEngineSuspend() *cli.Command { + cmd := &cmdRuleEngineSuspend{} + return &cli.Command{ + Name: "suspend", + Usage: "Suspend execution of rules", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdRuleEngineSuspend) do(ctx context.Context) error { + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/suspend", + }) +} diff --git a/internal/app/enaptercli/execute.go b/internal/app/enaptercli/execute.go index a873b37..7064748 100644 --- a/internal/app/enaptercli/execute.go +++ b/internal/app/enaptercli/execute.go @@ -24,6 +24,7 @@ func NewApp() *cli.App { buildCmdDevices(), buildCmdBlueprints(), buildCmdProvisioning(), + buildCmdRuleEngine(), } return app diff --git a/internal/app/enaptercli/testdata/helps/enapter b/internal/app/enaptercli/testdata/helps/enapter index 2b8c36d..0f85ffb 100644 --- a/internal/app/enaptercli/testdata/helps/enapter +++ b/internal/app/enaptercli/testdata/helps/enapter @@ -11,6 +11,7 @@ COMMANDS: devices Manage devices blueprints Manage blueprints provisioning Create devices of different types + rule-engine Manage the rule engine help, h Shows a list of commands or help for one command GLOBAL OPTIONS: diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine b/internal/app/enaptercli/testdata/helps/enapter rule-engine new file mode 100644 index 0000000..4afdb77 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine @@ -0,0 +1,15 @@ +NAME: + enaptercli.test rule-engine - Manage the rule engine + +USAGE: + enaptercli.test rule-engine command [command options] + +COMMANDS: + inspect Inspect the rule engine + suspend Suspend execution of rules + resume Resume execution of rules + rule Manage rules + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine inspect b/internal/app/enaptercli/testdata/helps/enapter rule-engine inspect new file mode 100644 index 0000000..a730ae4 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine inspect @@ -0,0 +1,13 @@ +NAME: + enaptercli.test rule-engine inspect - Inspect the rule engine + +USAGE: + enaptercli.test rule-engine inspect [command options] + +OPTIONS: + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume new file mode 100644 index 0000000..9b3128f --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume @@ -0,0 +1,13 @@ +NAME: + enaptercli.test rule-engine resume - Resume execution of rules + +USAGE: + enaptercli.test rule-engine resume [command options] + +OPTIONS: + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule new file mode 100644 index 0000000..f8f1aeb --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule @@ -0,0 +1,19 @@ +NAME: + enaptercli.test rule-engine rule - Manage rules + +USAGE: + enaptercli.test rule-engine rule command [command options] + +COMMANDS: + create Create a new rule + delete Delete one or more rules + disable Disable one or more rules + enable Enable one or more rules + inspect Inspect a rule + list List rules + update Update a rule + update-script Update the script of a rule + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create new file mode 100644 index 0000000..2dfa892 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create @@ -0,0 +1,19 @@ +NAME: + enaptercli.test rule-engine rule create - Create a new rule + +USAGE: + enaptercli.test rule-engine rule create [command options] + +OPTIONS: + --slug value Slug of a new rule + --name value Name of a new rule + --script value Path to a file containing the script code + --runtime-version value Version of a runtime to use for the script execution (default: 3) + --exec-interval value How often to execute the script. This option is only compatible with the runtime version 1 (default: 0s) + --disable Whether to disable a rule upon creation (default: false) + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete new file mode 100644 index 0000000..a327753 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete @@ -0,0 +1,14 @@ +NAME: + enaptercli.test rule-engine rule delete - Delete one or more rules + +USAGE: + enaptercli.test rule-engine rule delete [command options] + +OPTIONS: + --rule-id value [ --rule-id value ] Rule IDs or slugs + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable new file mode 100644 index 0000000..7d4ee55 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable @@ -0,0 +1,14 @@ +NAME: + enaptercli.test rule-engine rule disable - Disable one or more rules + +USAGE: + enaptercli.test rule-engine rule disable [command options] + +OPTIONS: + --rule-id value [ --rule-id value ] Rule IDs or slugs + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable new file mode 100644 index 0000000..228879d --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable @@ -0,0 +1,14 @@ +NAME: + enaptercli.test rule-engine rule enable - Enable one or more rules + +USAGE: + enaptercli.test rule-engine rule enable [command options] + +OPTIONS: + --rule-id value [ --rule-id value ] Rule IDs or slugs + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule inspect b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule inspect new file mode 100644 index 0000000..b7950a6 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule inspect @@ -0,0 +1,14 @@ +NAME: + enaptercli.test rule-engine rule inspect - Inspect a rule + +USAGE: + enaptercli.test rule-engine rule inspect [command options] + +OPTIONS: + --rule-id value Rule ID or slug + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list new file mode 100644 index 0000000..4bedc30 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list @@ -0,0 +1,13 @@ +NAME: + enaptercli.test rule-engine rule list - List rules + +USAGE: + enaptercli.test rule-engine rule list [command options] + +OPTIONS: + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update new file mode 100644 index 0000000..ce0a45d --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update @@ -0,0 +1,16 @@ +NAME: + enaptercli.test rule-engine rule update - Update a rule + +USAGE: + enaptercli.test rule-engine rule update [command options] + +OPTIONS: + --rule-id value Rule ID or slug to update + --slug value A new rule slug + --name value A new rule name + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script new file mode 100644 index 0000000..d3e32ec --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script @@ -0,0 +1,17 @@ +NAME: + enaptercli.test rule-engine rule update-script - Update the script of a rule + +USAGE: + enaptercli.test rule-engine rule update-script [command options] + +OPTIONS: + --rule-id value Rule ID or slug to update + --script value Path to a file containing the script code + --runtime-version value Version of a runtime to use for the script execution (default: 3) + --exec-interval value How often to execute the script. This option is only compatible with the runtime version 1 (default: 0s) + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend new file mode 100644 index 0000000..2e177b1 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend @@ -0,0 +1,13 @@ +NAME: + enaptercli.test rule-engine suspend - Suspend execution of rules + +USAGE: + enaptercli.test rule-engine suspend [command options] + +OPTIONS: + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + From 01eede6c6b00a9e7fd4dce81caee6023423c41d2 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 27 Dec 2024 06:19:53 +0300 Subject: [PATCH 11/88] feat: require name for rule, not slug --- internal/app/enaptercli/cmd_rule_engine_rule_create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_create.go b/internal/app/enaptercli/cmd_rule_engine_rule_create.go index 1603bb6..6024010 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_create.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_create.go @@ -43,12 +43,12 @@ func (c *cmdRuleEngineRuleCreate) Flags() []cli.Flag { Name: "slug", Usage: "Slug of a new rule", Destination: &c.slug, - Required: true, }, &cli.StringFlag{ Name: "name", Usage: "Name of a new rule", Destination: &c.name, + Required: true, }, &cli.StringFlag{ Name: "script", From 0f7b1b92899fb695bc5b4e2ae71b2c5fa5361223 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 27 Dec 2024 17:38:03 +0300 Subject: [PATCH 12/88] feat: set req body content type --- internal/app/enaptercli/cmd_base.go | 2 ++ internal/app/enaptercli/cmd_devices_assign_blueprint.go | 7 ++++--- internal/app/enaptercli/cmd_provisioning_lua_device.go | 7 ++++--- internal/app/enaptercli/cmd_provisioning_standalone.go | 7 ++++--- internal/app/enaptercli/cmd_rule_engine_rule_create.go | 7 ++++--- internal/app/enaptercli/cmd_rule_engine_rule_delete.go | 7 ++++--- internal/app/enaptercli/cmd_rule_engine_rule_disable.go | 7 ++++--- internal/app/enaptercli/cmd_rule_engine_rule_enable.go | 7 ++++--- internal/app/enaptercli/cmd_rule_engine_rule_update.go | 7 ++++--- .../app/enaptercli/cmd_rule_engine_rule_update_script.go | 7 ++++--- internal/app/enaptercli/content_types.go | 5 +++++ 11 files changed, 43 insertions(+), 27 deletions(-) create mode 100644 internal/app/enaptercli/content_types.go diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 9a9b9c7..d6801fd 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -69,6 +69,7 @@ type doHTTPRequestParams struct { Path string Query url.Values Body io.Reader + ContentType string RespProcessor func(*http.Response) error } @@ -79,6 +80,7 @@ func (c *cmdBase) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) erro } req.Header.Add("X-Enapter-Auth-Token", c.token) + req.Header.Set("Content-Type", p.ContentType) req.URL.RawQuery = p.Query.Encode() resp, err := c.httpClient.Do(req) diff --git a/internal/app/enaptercli/cmd_devices_assign_blueprint.go b/internal/app/enaptercli/cmd_devices_assign_blueprint.go index f455119..53ae6da 100644 --- a/internal/app/enaptercli/cmd_devices_assign_blueprint.go +++ b/internal/app/enaptercli/cmd_devices_assign_blueprint.go @@ -48,8 +48,9 @@ func (c *cmdDevicesAssignBlueprint) do(ctx context.Context) error { return fmt.Errorf("build request: %w", err) } return c.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodPost, - Path: "/assign_blueprint", - Body: bytes.NewReader(body), + Method: http.MethodPost, + Path: "/assign_blueprint", + Body: bytes.NewReader(body), + ContentType: contentTypeJSON, }) } diff --git a/internal/app/enaptercli/cmd_provisioning_lua_device.go b/internal/app/enaptercli/cmd_provisioning_lua_device.go index 6764a87..795a628 100644 --- a/internal/app/enaptercli/cmd_provisioning_lua_device.go +++ b/internal/app/enaptercli/cmd_provisioning_lua_device.go @@ -64,8 +64,9 @@ func (c *cmdProvisioningLua) do(ctx context.Context) error { return fmt.Errorf("build request: %w", err) } return c.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodPost, - Path: "/provisioning/lua_device", - Body: bytes.NewReader(body), + Method: http.MethodPost, + Path: "/provisioning/lua_device", + Body: bytes.NewReader(body), + ContentType: contentTypeJSON, }) } diff --git a/internal/app/enaptercli/cmd_provisioning_standalone.go b/internal/app/enaptercli/cmd_provisioning_standalone.go index d141160..7854023 100644 --- a/internal/app/enaptercli/cmd_provisioning_standalone.go +++ b/internal/app/enaptercli/cmd_provisioning_standalone.go @@ -56,8 +56,9 @@ func (c *cmdProvisioningStandalone) do(ctx context.Context) error { return fmt.Errorf("build request: %w", err) } return c.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodPost, - Path: "/provisioning/standalone", - Body: bytes.NewReader(body), + Method: http.MethodPost, + Path: "/provisioning/standalone", + Body: bytes.NewReader(body), + ContentType: contentTypeJSON, }) } diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_create.go b/internal/app/enaptercli/cmd_rule_engine_rule_create.go index 6024010..69a5a15 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_create.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_create.go @@ -101,8 +101,9 @@ func (c *cmdRuleEngineRuleCreate) do(ctx context.Context) error { } return c.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodPost, - Path: "", - Body: bytes.NewReader(body), + Method: http.MethodPost, + Path: "", + Body: bytes.NewReader(body), + ContentType: contentTypeJSON, }) } diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_delete.go b/internal/app/enaptercli/cmd_rule_engine_rule_delete.go index 87de852..72b84e3 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_delete.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_delete.go @@ -50,8 +50,9 @@ func (c *cmdRuleEngineRuleDelete) do(ctx context.Context) error { return fmt.Errorf("build request: %w", err) } return c.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodPost, - Path: "/batch_delete", - Body: bytes.NewReader(body), + Method: http.MethodPost, + Path: "/batch_delete", + Body: bytes.NewReader(body), + ContentType: contentTypeJSON, }) } diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_disable.go b/internal/app/enaptercli/cmd_rule_engine_rule_disable.go index 03711bd..3a30258 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_disable.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_disable.go @@ -52,8 +52,9 @@ func (c *cmdRuleEngineRuleDisable) do(ctx context.Context) error { fmt.Println(c.ruleIDs) return c.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodPost, - Path: "/batch_disable", - Body: bytes.NewReader(body), + Method: http.MethodPost, + Path: "/batch_disable", + Body: bytes.NewReader(body), + ContentType: contentTypeJSON, }) } diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_enable.go b/internal/app/enaptercli/cmd_rule_engine_rule_enable.go index 18cbaa9..6338f19 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_enable.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_enable.go @@ -50,8 +50,9 @@ func (c *cmdRuleEngineRuleEnable) do(ctx context.Context) error { return fmt.Errorf("build request: %w", err) } return c.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodPost, - Path: "/batch_enable", - Body: bytes.NewReader(body), + Method: http.MethodPost, + Path: "/batch_enable", + Body: bytes.NewReader(body), + ContentType: contentTypeJSON, }) } diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update.go b/internal/app/enaptercli/cmd_rule_engine_rule_update.go index d006e38..74a63d9 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_update.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update.go @@ -78,8 +78,9 @@ func (c *cmdRuleEngineRuleUpdate) do(cliCtx *cli.Context) error { } return c.doHTTPRequest(cliCtx.Context, doHTTPRequestParams{ - Method: http.MethodPatch, - Path: "/" + c.ruleID, - Body: bytes.NewReader(body), + Method: http.MethodPatch, + Path: "/" + c.ruleID, + Body: bytes.NewReader(body), + ContentType: contentTypeJSON, }) } diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go index 0dfd6ba..513c116 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go @@ -84,8 +84,9 @@ func (c *cmdRuleEngineRuleUpdateScript) do(ctx context.Context) error { } return c.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodPost, - Path: "/" + c.ruleID + "/update_script", - Body: bytes.NewReader(body), + Method: http.MethodPost, + Path: "/" + c.ruleID + "/update_script", + Body: bytes.NewReader(body), + ContentType: contentTypeJSON, }) } diff --git a/internal/app/enaptercli/content_types.go b/internal/app/enaptercli/content_types.go new file mode 100644 index 0000000..5431e71 --- /dev/null +++ b/internal/app/enaptercli/content_types.go @@ -0,0 +1,5 @@ +package enaptercli + +const ( + contentTypeJSON = "application/json" +) From 460dda3eda86e7469c6833e226ee0acede75ecda Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 27 Dec 2024 17:38:26 +0300 Subject: [PATCH 13/88] feat: add verbose mode --- internal/app/enaptercli/cmd_base.go | 27 +++++++++++++++++++ .../helps/enapter blueprints download | 1 + .../testdata/helps/enapter blueprints inspect | 1 + .../enapter blueprints profiles download | 1 + .../helps/enapter blueprints profiles upload | 1 + .../testdata/helps/enapter blueprints upload | 1 + .../helps/enapter devices assign_blueprint | 1 + .../testdata/helps/enapter devices logs | 1 + .../testdata/helps/enapter devices logsf | 1 + .../helps/enapter provisioning lua_device | 1 + .../helps/enapter provisioning standalone | 1 + .../helps/enapter rule-engine inspect | 1 + .../testdata/helps/enapter rule-engine resume | 1 + .../helps/enapter rule-engine rule create | 1 + .../helps/enapter rule-engine rule delete | 1 + .../helps/enapter rule-engine rule disable | 1 + .../helps/enapter rule-engine rule enable | 1 + .../helps/enapter rule-engine rule inspect | 1 + .../helps/enapter rule-engine rule list | 1 + .../helps/enapter rule-engine rule update | 1 + .../enapter rule-engine rule update-script | 1 + .../helps/enapter rule-engine suspend | 1 + 22 files changed, 48 insertions(+) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index d6801fd..529a0b2 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -1,7 +1,9 @@ package enaptercli import ( + "bytes" "context" + "encoding/base64" "encoding/json" "errors" "fmt" @@ -14,6 +16,7 @@ import ( ) type cmdBase struct { + verbose bool token string apiHost string writer io.Writer @@ -43,6 +46,11 @@ func (c *cmdBase) Flags() []cli.Flag { return nil }, }, + &cli.BoolFlag{ + Name: "verbose", + Usage: "log extra details about operation", + Destination: &c.verbose, + }, } } @@ -83,6 +91,25 @@ func (c *cmdBase) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) erro req.Header.Set("Content-Type", p.ContentType) req.URL.RawQuery = p.Query.Encode() + if c.verbose { + bb := &bytes.Buffer{} + if _, err := io.Copy(bb, req.Body); err != nil { + return fmt.Errorf("reading body for verbose log: %w", err) + } + if err := req.Body.Close(); err != nil { + return fmt.Errorf("closing body for verbose log: %w", err) + } + req.Body = io.NopCloser(bb) + + bodyStr := bb.String() + if p.ContentType != contentTypeJSON { + bodyStr = base64.RawStdEncoding.EncodeToString(bb.Bytes()) + } + + fmt.Fprintf(c.writer, "== Do http request %s %s\n", p.Method, req.URL.String()) + fmt.Fprintf(c.writer, "=== Begin body\n%s\n=== End body\n", bodyStr) + } + resp, err := c.httpClient.Do(req) if err != nil { return fmt.Errorf("do http request: %w", err) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints download b/internal/app/enaptercli/testdata/helps/enapter blueprints download index ed2810b..a963be9 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints download @@ -5,6 +5,7 @@ USAGE: enaptercli.test blueprints download [command options] OPTIONS: + --verbose log extra details about operation (default: false) --blueprint-id value, -b value blueprint name or ID to download --output value, -o value blueprint file name to save --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints inspect b/internal/app/enaptercli/testdata/helps/enapter blueprints inspect index 51a96cb..af0e6ab 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints inspect +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints inspect @@ -5,6 +5,7 @@ USAGE: enaptercli.test blueprints inspect [command options] OPTIONS: + --verbose log extra details about operation (default: false) --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints profiles download b/internal/app/enaptercli/testdata/helps/enapter blueprints profiles download index c03a06e..1452f53 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints profiles download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints profiles download @@ -5,6 +5,7 @@ USAGE: enaptercli.test blueprints profiles download [command options] OPTIONS: + --verbose log extra details about operation (default: false) --output value, -o value file name to save --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints profiles upload b/internal/app/enaptercli/testdata/helps/enapter blueprints profiles upload index ed2d1e5..bcc1f05 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints profiles upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints profiles upload @@ -5,6 +5,7 @@ USAGE: enaptercli.test blueprints profiles upload [command options] profiles zip file path OPTIONS: + --verbose log extra details about operation (default: false) --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints upload b/internal/app/enaptercli/testdata/helps/enapter blueprints upload index dc5dbbd..35af3f0 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprints upload @@ -5,6 +5,7 @@ USAGE: enaptercli.test blueprints upload [command options] OPTIONS: + --verbose log extra details about operation (default: false) --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint b/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint index 1ee8c1a..dbf362c 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint @@ -5,6 +5,7 @@ USAGE: enaptercli.test devices assign_blueprint [command options] OPTIONS: + --verbose log extra details about operation (default: false) --device-id value, -d value device ID --blueprint-id value, -b value blueprint ID to assign --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter devices logs b/internal/app/enaptercli/testdata/helps/enapter devices logs index 8437511..cf9abae 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices logs +++ b/internal/app/enaptercli/testdata/helps/enapter devices logs @@ -5,6 +5,7 @@ USAGE: enaptercli.test devices logs [command options] OPTIONS: + --verbose log extra details about operation (default: false) --device-id value, -d value device ID --from value, -f value from timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z) --to value, -t value to timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z) diff --git a/internal/app/enaptercli/testdata/helps/enapter devices logsf b/internal/app/enaptercli/testdata/helps/enapter devices logsf index 9bbdc8b..98a85ed 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices logsf +++ b/internal/app/enaptercli/testdata/helps/enapter devices logsf @@ -5,6 +5,7 @@ USAGE: enaptercli.test devices logsf [command options] OPTIONS: + --verbose log extra details about operation (default: false) --device-id value, -d value device ID --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device b/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device index 2b326c9..0a4a7d7 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device @@ -5,6 +5,7 @@ USAGE: enaptercli.test provisioning lua_device [command options] OPTIONS: + --verbose log extra details about operation (default: false) --runtime-id value, -r value runtime UCM device ID where to run a new Lua device --device-name value, -n value name of a new Lua device --blueprint-id value, -b value blueprint ID of a new Lua device diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone index b723498..e8ce45f 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone @@ -5,6 +5,7 @@ USAGE: enaptercli.test provisioning standalone [command options] OPTIONS: + --verbose log extra details about operation (default: false) --site-id value, -s value site ID where to craate device --device-name value, -n value name for a new device --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine inspect b/internal/app/enaptercli/testdata/helps/enapter rule-engine inspect index a730ae4..0abc976 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine inspect +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine inspect @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine inspect [command options] OPTIONS: + --verbose log extra details about operation (default: false) --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume index 9b3128f..ba1032b 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine resume [command options] OPTIONS: + --verbose log extra details about operation (default: false) --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create index 2dfa892..d5448cf 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine rule create [command options] OPTIONS: + --verbose log extra details about operation (default: false) --slug value Slug of a new rule --name value Name of a new rule --script value Path to a file containing the script code diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete index a327753..11e1f56 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine rule delete [command options] OPTIONS: + --verbose log extra details about operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable index 7d4ee55..0aa5581 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine rule disable [command options] OPTIONS: + --verbose log extra details about operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable index 228879d..487c801 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine rule enable [command options] OPTIONS: + --verbose log extra details about operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule inspect b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule inspect index b7950a6..a829366 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule inspect +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule inspect @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine rule inspect [command options] OPTIONS: + --verbose log extra details about operation (default: false) --rule-id value Rule ID or slug --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list index 4bedc30..7f72e22 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine rule list [command options] OPTIONS: + --verbose log extra details about operation (default: false) --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update index ce0a45d..10297ab 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine rule update [command options] OPTIONS: + --verbose log extra details about operation (default: false) --rule-id value Rule ID or slug to update --slug value A new rule slug --name value A new rule name diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script index d3e32ec..315c7e7 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine rule update-script [command options] OPTIONS: + --verbose log extra details about operation (default: false) --rule-id value Rule ID or slug to update --script value Path to a file containing the script code --runtime-version value Version of a runtime to use for the script execution (default: 3) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend index 2e177b1..3f5f4ae 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine suspend [command options] OPTIONS: + --verbose log extra details about operation (default: false) --help, -h show help ENVIRONMENT VARIABLES: From f87ec201330ae4879612bed857e1dbd50a7bb39c Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 27 Dec 2024 17:42:44 +0300 Subject: [PATCH 14/88] bug: clean path before zip dir --- internal/app/enaptercli/execute.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/app/enaptercli/execute.go b/internal/app/enaptercli/execute.go index 7064748..761ba1f 100644 --- a/internal/app/enaptercli/execute.go +++ b/internal/app/enaptercli/execute.go @@ -34,6 +34,7 @@ func zipDir(path string) ([]byte, error) { buf := &bytes.Buffer{} zw := zip.NewWriter(buf) + path = filepath.Clean(path) err := filepath.WalkDir(path, func(filePath string, entry os.DirEntry, err error) error { if err != nil { return err From ede09daad94aa59629a4f1c9ceb8ed00bc1cd25f Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 30 Dec 2024 05:42:58 +0300 Subject: [PATCH 15/88] refactor: move device-id option into devices subcommand --- internal/app/enaptercli/cmd_devices.go | 14 +------------- .../app/enaptercli/cmd_devices_assign_blueprint.go | 9 ++++++++- internal/app/enaptercli/cmd_devices_logs.go | 11 +++++++++-- internal/app/enaptercli/cmd_devices_logsf.go | 14 +++++++++++++- 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/internal/app/enaptercli/cmd_devices.go b/internal/app/enaptercli/cmd_devices.go index c17627c..b3b6de7 100644 --- a/internal/app/enaptercli/cmd_devices.go +++ b/internal/app/enaptercli/cmd_devices.go @@ -12,7 +12,6 @@ import ( type cmdDevices struct { cmdBase - deviceID string } func buildCmdDevices() *cli.Command { @@ -27,19 +26,8 @@ func buildCmdDevices() *cli.Command { } } -func (c *cmdDevices) Flags() []cli.Flag { - flags := c.cmdBase.Flags() - return append(flags, &cli.StringFlag{ - Name: "device-id", - Aliases: []string{"d"}, - Usage: "device ID", - Destination: &c.deviceID, - Required: true, - }) -} - func (c *cmdDevices) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { - path, err := url.JoinPath("/devices", c.deviceID, p.Path) + path, err := url.JoinPath("/devices", p.Path) if err != nil { return fmt.Errorf("join path: %w", err) } diff --git a/internal/app/enaptercli/cmd_devices_assign_blueprint.go b/internal/app/enaptercli/cmd_devices_assign_blueprint.go index 53ae6da..01de6d7 100644 --- a/internal/app/enaptercli/cmd_devices_assign_blueprint.go +++ b/internal/app/enaptercli/cmd_devices_assign_blueprint.go @@ -12,6 +12,7 @@ import ( type cmdDevicesAssignBlueprint struct { cmdDevices + deviceID string blueprintID string } @@ -32,6 +33,12 @@ func buildCmdDevicesAssignBlueprint() *cli.Command { func (c *cmdDevicesAssignBlueprint) Flags() []cli.Flag { flags := c.cmdDevices.Flags() return append(flags, &cli.StringFlag{ + Name: "device-id", + Aliases: []string{"d"}, + Usage: "device ID", + Destination: &c.deviceID, + Required: true, + }, &cli.StringFlag{ Name: "blueprint-id", Aliases: []string{"b"}, Usage: "blueprint ID to assign", @@ -49,7 +56,7 @@ func (c *cmdDevicesAssignBlueprint) do(ctx context.Context) error { } return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodPost, - Path: "/assign_blueprint", + Path: "/" + c.deviceID + "/assign_blueprint", Body: bytes.NewReader(body), ContentType: contentTypeJSON, }) diff --git a/internal/app/enaptercli/cmd_devices_logs.go b/internal/app/enaptercli/cmd_devices_logs.go index 9e5a6d2..54a14ae 100644 --- a/internal/app/enaptercli/cmd_devices_logs.go +++ b/internal/app/enaptercli/cmd_devices_logs.go @@ -14,6 +14,7 @@ import ( type cmdDevicesLogs struct { cmdDevices + deviceID string from cli.Timestamp to cli.Timestamp offset int @@ -39,7 +40,13 @@ func buildCmdDevicesLogs() *cli.Command { func (c *cmdDevicesLogs) Flags() []cli.Flag { flags := c.cmdDevices.Flags() - return append(flags, &cli.TimestampFlag{ + return append(flags, &cli.StringFlag{ + Name: "device-id", + Aliases: []string{"d"}, + Usage: "device ID", + Destination: &c.deviceID, + Required: true, + }, &cli.TimestampFlag{ Name: "from", Aliases: []string{"f"}, Usage: "from timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z)", @@ -115,7 +122,7 @@ func (c *cmdDevicesLogs) do(ctx context.Context) error { return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, - Path: "/logs", + Path: "/" + c.deviceID + "/logs", Query: query, //nolint:bodyclose //body is closed in doHTTPRequest RespProcessor: okRespBodyProcessor(func(body io.Reader) error { diff --git a/internal/app/enaptercli/cmd_devices_logsf.go b/internal/app/enaptercli/cmd_devices_logsf.go index f9d9d02..9b9ae63 100644 --- a/internal/app/enaptercli/cmd_devices_logsf.go +++ b/internal/app/enaptercli/cmd_devices_logsf.go @@ -15,6 +15,7 @@ import ( type cmdDevicesLogsf struct { cmdDevices + deviceID string } func buildCmdDevicesLogsf() *cli.Command { @@ -31,6 +32,17 @@ func buildCmdDevicesLogsf() *cli.Command { } } +func (c *cmdDevicesLogsf) Flags() []cli.Flag { + flags := c.cmdBase.Flags() + return append(flags, &cli.StringFlag{ + Name: "device-id", + Aliases: []string{"d"}, + Usage: "device ID", + Destination: &c.deviceID, + Required: true, + }) +} + func (c *cmdDevicesLogsf) do(ctx context.Context) error { const singleRequestLimit = 10 @@ -45,7 +57,7 @@ func (c *cmdDevicesLogsf) do(ctx context.Context) error { retryNow := false err := c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, - Path: "/logs", + Path: "/" + c.deviceID + "/logs", Query: query, //nolint:bodyclose //body is closed in doHTTPRequest RespProcessor: okRespBodyProcessor(func(body io.Reader) error { From 1113090066899fcda2e48d0fa9dae05f98de22a2 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 30 Dec 2024 06:07:10 +0300 Subject: [PATCH 16/88] feat: fix provisioning command names --- internal/app/enaptercli/cmd_provisioning_lua_device.go | 2 +- internal/app/enaptercli/testdata/helps/enapter provisioning | 2 +- ...rovisioning lua_device => enapter provisioning lua-device} | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename internal/app/enaptercli/testdata/helps/{enapter provisioning lua_device => enapter provisioning lua-device} (80%) diff --git a/internal/app/enaptercli/cmd_provisioning_lua_device.go b/internal/app/enaptercli/cmd_provisioning_lua_device.go index 795a628..dc7c64e 100644 --- a/internal/app/enaptercli/cmd_provisioning_lua_device.go +++ b/internal/app/enaptercli/cmd_provisioning_lua_device.go @@ -20,7 +20,7 @@ type cmdProvisioningLua struct { func buildCmdProvisioningLua() *cli.Command { cmd := &cmdProvisioningLua{} return &cli.Command{ - Name: "lua_device", + Name: "lua-device", Usage: "Create a new Lua device", CustomHelpTemplate: cmd.HelpTemplate(), Flags: cmd.Flags(), diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning b/internal/app/enaptercli/testdata/helps/enapter provisioning index c3fe2ef..5e26987 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning @@ -6,7 +6,7 @@ USAGE: COMMANDS: standalone Create a new standalone device - lua_device Create a new Lua device + lua-device Create a new Lua device help, h Shows a list of commands or help for one command OPTIONS: diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device b/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device similarity index 80% rename from internal/app/enaptercli/testdata/helps/enapter provisioning lua_device rename to internal/app/enaptercli/testdata/helps/enapter provisioning lua-device index 0a4a7d7..c690477 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning lua_device +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device @@ -1,8 +1,8 @@ NAME: - enaptercli.test provisioning lua_device - Create a new Lua device + enaptercli.test provisioning lua-device - Create a new Lua device USAGE: - enaptercli.test provisioning lua_device [command options] + enaptercli.test provisioning lua-device [command options] OPTIONS: --verbose log extra details about operation (default: false) From 9d5c75f970a4eb71a08fcbe871b6b01a1554c5e1 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 30 Dec 2024 06:09:49 +0300 Subject: [PATCH 17/88] feat: fix blueprints command names --- internal/app/enaptercli/cmd_blueprints.go | 2 +- internal/app/enaptercli/testdata/helps/enapter | 2 +- .../testdata/helps/{enapter blueprints => enapter blueprint} | 4 ++-- ...enapter blueprints download => enapter blueprint download} | 4 ++-- .../{enapter blueprints inspect => enapter blueprint inspect} | 4 ++-- ... profiles download => enapter blueprint profiles download} | 4 ++-- ...ints profiles upload => enapter blueprint profiles upload} | 4 ++-- .../{enapter blueprints upload => enapter blueprint upload} | 4 ++-- .../testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl | 1 + .../{blueprints_inspect_by_id => blueprint_inspect_by_id}/out | 0 .../req_0 | 0 .../resp_0 | 0 .../testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl | 1 + .../out | 0 .../req_0 | 0 .../resp_0 | 0 .../testdata/http_req_resp/blueprints_inspect_by_id/cmd.tmpl | 1 - .../http_req_resp/blueprints_inspect_by_name/cmd.tmpl | 1 - 18 files changed, 16 insertions(+), 16 deletions(-) rename internal/app/enaptercli/testdata/helps/{enapter blueprints => enapter blueprint} (74%) rename internal/app/enaptercli/testdata/helps/{enapter blueprints download => enapter blueprint download} (76%) rename internal/app/enaptercli/testdata/helps/{enapter blueprints inspect => enapter blueprint inspect} (65%) rename internal/app/enaptercli/testdata/helps/{enapter blueprints profiles download => enapter blueprint profiles download} (69%) rename internal/app/enaptercli/testdata/helps/{enapter blueprints profiles upload => enapter blueprint profiles upload} (61%) rename internal/app/enaptercli/testdata/helps/{enapter blueprints upload => enapter blueprint upload} (60%) create mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl rename internal/app/enaptercli/testdata/http_req_resp/{blueprints_inspect_by_id => blueprint_inspect_by_id}/out (100%) rename internal/app/enaptercli/testdata/http_req_resp/{blueprints_inspect_by_id => blueprint_inspect_by_id}/req_0 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{blueprints_inspect_by_id => blueprint_inspect_by_id}/resp_0 (100%) create mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl rename internal/app/enaptercli/testdata/http_req_resp/{blueprints_inspect_by_name => blueprint_inspect_by_name}/out (100%) rename internal/app/enaptercli/testdata/http_req_resp/{blueprints_inspect_by_name => blueprint_inspect_by_name}/req_0 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{blueprints_inspect_by_name => blueprint_inspect_by_name}/resp_0 (100%) delete mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/cmd.tmpl delete mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/cmd.tmpl diff --git a/internal/app/enaptercli/cmd_blueprints.go b/internal/app/enaptercli/cmd_blueprints.go index 77a7aad..9acbd1b 100644 --- a/internal/app/enaptercli/cmd_blueprints.go +++ b/internal/app/enaptercli/cmd_blueprints.go @@ -12,7 +12,7 @@ type cmdBlueprints struct { func buildCmdBlueprints() *cli.Command { return &cli.Command{ - Name: "blueprints", + Name: "blueprint", Usage: "Manage blueprints", Subcommands: []*cli.Command{ buildCmdBlueprintsProfiles(), diff --git a/internal/app/enaptercli/testdata/helps/enapter b/internal/app/enaptercli/testdata/helps/enapter index 0f85ffb..d5cdb68 100644 --- a/internal/app/enaptercli/testdata/helps/enapter +++ b/internal/app/enaptercli/testdata/helps/enapter @@ -9,7 +9,7 @@ DESCRIPTION: COMMANDS: devices Manage devices - blueprints Manage blueprints + blueprint Manage blueprints provisioning Create devices of different types rule-engine Manage the rule engine help, h Shows a list of commands or help for one command diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints b/internal/app/enaptercli/testdata/helps/enapter blueprint similarity index 74% rename from internal/app/enaptercli/testdata/helps/enapter blueprints rename to internal/app/enaptercli/testdata/helps/enapter blueprint index a7b2073..0260052 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint @@ -1,8 +1,8 @@ NAME: - enaptercli.test blueprints - Manage blueprints + enaptercli.test blueprint - Manage blueprints USAGE: - enaptercli.test blueprints command [command options] + enaptercli.test blueprint command [command options] COMMANDS: profiles Manage blueprints profiles diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints download b/internal/app/enaptercli/testdata/helps/enapter blueprint download similarity index 76% rename from internal/app/enaptercli/testdata/helps/enapter blueprints download rename to internal/app/enaptercli/testdata/helps/enapter blueprint download index a963be9..113afb9 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint download @@ -1,8 +1,8 @@ NAME: - enaptercli.test blueprints download - Download blueprint zip from Platform + enaptercli.test blueprint download - Download blueprint zip from Platform USAGE: - enaptercli.test blueprints download [command options] + enaptercli.test blueprint download [command options] OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints inspect b/internal/app/enaptercli/testdata/helps/enapter blueprint inspect similarity index 65% rename from internal/app/enaptercli/testdata/helps/enapter blueprints inspect rename to internal/app/enaptercli/testdata/helps/enapter blueprint inspect index af0e6ab..f0b0dbd 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints inspect +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint inspect @@ -1,8 +1,8 @@ NAME: - enaptercli.test blueprints inspect - Get blueprint metainfo + enaptercli.test blueprint inspect - Get blueprint metainfo USAGE: - enaptercli.test blueprints inspect [command options] + enaptercli.test blueprint inspect [command options] OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints profiles download b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download similarity index 69% rename from internal/app/enaptercli/testdata/helps/enapter blueprints profiles download rename to internal/app/enaptercli/testdata/helps/enapter blueprint profiles download index 1452f53..391a1b0 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints profiles download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download @@ -1,8 +1,8 @@ NAME: - enaptercli.test blueprints profiles download - Download profiles zip from Platform + enaptercli.test blueprint profiles download - Download profiles zip from Platform USAGE: - enaptercli.test blueprints profiles download [command options] + enaptercli.test blueprint profiles download [command options] OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints profiles upload b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload similarity index 61% rename from internal/app/enaptercli/testdata/helps/enapter blueprints profiles upload rename to internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload index bcc1f05..5c33dbe 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints profiles upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload @@ -1,8 +1,8 @@ NAME: - enaptercli.test blueprints profiles upload - Upload profiles into Platform + enaptercli.test blueprint profiles upload - Upload profiles into Platform USAGE: - enaptercli.test blueprints profiles upload [command options] profiles zip file path + enaptercli.test blueprint profiles upload [command options] profiles zip file path OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprints upload b/internal/app/enaptercli/testdata/helps/enapter blueprint upload similarity index 60% rename from internal/app/enaptercli/testdata/helps/enapter blueprints upload rename to internal/app/enaptercli/testdata/helps/enapter blueprint upload index 35af3f0..6c88199 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprints upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint upload @@ -1,8 +1,8 @@ NAME: - enaptercli.test blueprints upload - Upload blueprint directory into Platform + enaptercli.test blueprint upload - Upload blueprint directory into Platform USAGE: - enaptercli.test blueprints upload [command options] + enaptercli.test blueprint upload [command options] OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl new file mode 100644 index 0000000..b30e692 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl @@ -0,0 +1 @@ +enapter3 blueprint inspect {{.BaseFlags}} cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/out b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/out similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/out rename to internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/out diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/req_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/req_0 rename to internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/req_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/resp_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/resp_0 rename to internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/resp_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl new file mode 100644 index 0000000..b0388af --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl @@ -0,0 +1 @@ +enapter3 blueprint inspect {{.BaseFlags}} test_blueprint_name diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/out b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/out similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/out rename to internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/out diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/req_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/req_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/req_0 rename to internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/req_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/resp_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/resp_0 rename to internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/resp_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/cmd.tmpl deleted file mode 100644 index 83e0a90..0000000 --- a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_id/cmd.tmpl +++ /dev/null @@ -1 +0,0 @@ -enapter3 blueprints inspect {{.BaseFlags}} cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/cmd.tmpl deleted file mode 100644 index 12ab423..0000000 --- a/internal/app/enaptercli/testdata/http_req_resp/blueprints_inspect_by_name/cmd.tmpl +++ /dev/null @@ -1 +0,0 @@ -enapter3 blueprints inspect {{.BaseFlags}} test_blueprint_name From 0157302e8cf2fe9b081193644d3b73944ab397f0 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 30 Dec 2024 06:06:13 +0300 Subject: [PATCH 18/88] feat: fix devices command names --- .../enaptercli/{cmd_devices.go => cmd_device.go} | 16 +++++++++++++++- ...ueprint.go => cmd_device_assign_blueprint.go} | 2 +- .../{cmd_devices_logs.go => cmd_device_logs.go} | 0 ...{cmd_devices_logsf.go => cmd_device_logsf.go} | 0 internal/app/enaptercli/testdata/helps/enapter | 2 +- .../helps/{enapter devices => enapter device} | 6 +++--- ...blueprint => enapter device assign-blueprint} | 4 ++-- ...{enapter devices logs => enapter device logs} | 4 ++-- ...napter devices logsf => enapter device logsf} | 4 ++-- .../device_assign_blueprint/cmd.tmpl | 1 + .../out | 0 .../req_0 | 0 .../resp_0 | 0 .../devices_assign_blueprint/cmd.tmpl | 1 - 14 files changed, 27 insertions(+), 13 deletions(-) rename internal/app/enaptercli/{cmd_devices.go => cmd_device.go} (70%) rename internal/app/enaptercli/{cmd_devices_assign_blueprint.go => cmd_device_assign_blueprint.go} (97%) rename internal/app/enaptercli/{cmd_devices_logs.go => cmd_device_logs.go} (100%) rename internal/app/enaptercli/{cmd_devices_logsf.go => cmd_device_logsf.go} (100%) rename internal/app/enaptercli/testdata/helps/{enapter devices => enapter device} (58%) rename internal/app/enaptercli/testdata/helps/{enapter devices assign_blueprint => enapter device assign-blueprint} (75%) rename internal/app/enaptercli/testdata/helps/{enapter devices logs => enapter device logs} (90%) rename internal/app/enaptercli/testdata/helps/{enapter devices logsf => enapter device logsf} (76%) create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/cmd.tmpl rename internal/app/enaptercli/testdata/http_req_resp/{devices_assign_blueprint => device_assign_blueprint}/out (100%) rename internal/app/enaptercli/testdata/http_req_resp/{devices_assign_blueprint => device_assign_blueprint}/req_0 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{devices_assign_blueprint => device_assign_blueprint}/resp_0 (100%) delete mode 100644 internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/cmd.tmpl diff --git a/internal/app/enaptercli/cmd_devices.go b/internal/app/enaptercli/cmd_device.go similarity index 70% rename from internal/app/enaptercli/cmd_devices.go rename to internal/app/enaptercli/cmd_device.go index b3b6de7..0b77941 100644 --- a/internal/app/enaptercli/cmd_devices.go +++ b/internal/app/enaptercli/cmd_device.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/url" + "slices" "github.com/urfave/cli/v2" ) @@ -16,7 +17,7 @@ type cmdDevices struct { func buildCmdDevices() *cli.Command { return &cli.Command{ - Name: "devices", + Name: "device", Usage: "Manage devices", Subcommands: []*cli.Command{ buildCmdDevicesAssignBlueprint(), @@ -52,3 +53,16 @@ func (c *cmdDevices) parseAndDumpDeviceLogs(body io.Reader) (int, error) { } return len(resp.Logs), nil } + +func (c *cmdDevices) validateExpandFlag(cliCtx *cli.Context) error { + supportedFields := []string{"connectivity", "manifest", "properties", "communication_info", "site"} + slices.Sort(supportedFields) + + for _, field := range cliCtx.StringSlice("expand") { + if _, ok := slices.BinarySearch(supportedFields, field); !ok { + return fmt.Errorf("%w: %s is not supported by expand, should be one of %s", + errUnsupportedFlagValue, field, supportedFields) + } + } + return nil +} diff --git a/internal/app/enaptercli/cmd_devices_assign_blueprint.go b/internal/app/enaptercli/cmd_device_assign_blueprint.go similarity index 97% rename from internal/app/enaptercli/cmd_devices_assign_blueprint.go rename to internal/app/enaptercli/cmd_device_assign_blueprint.go index 01de6d7..752087d 100644 --- a/internal/app/enaptercli/cmd_devices_assign_blueprint.go +++ b/internal/app/enaptercli/cmd_device_assign_blueprint.go @@ -19,7 +19,7 @@ type cmdDevicesAssignBlueprint struct { func buildCmdDevicesAssignBlueprint() *cli.Command { cmd := &cmdDevicesAssignBlueprint{} return &cli.Command{ - Name: "assign_blueprint", + Name: "assign-blueprint", Usage: "Assign blueprint to device", CustomHelpTemplate: cmd.HelpTemplate(), Flags: cmd.Flags(), diff --git a/internal/app/enaptercli/cmd_devices_logs.go b/internal/app/enaptercli/cmd_device_logs.go similarity index 100% rename from internal/app/enaptercli/cmd_devices_logs.go rename to internal/app/enaptercli/cmd_device_logs.go diff --git a/internal/app/enaptercli/cmd_devices_logsf.go b/internal/app/enaptercli/cmd_device_logsf.go similarity index 100% rename from internal/app/enaptercli/cmd_devices_logsf.go rename to internal/app/enaptercli/cmd_device_logsf.go diff --git a/internal/app/enaptercli/testdata/helps/enapter b/internal/app/enaptercli/testdata/helps/enapter index d5cdb68..d3a1e39 100644 --- a/internal/app/enaptercli/testdata/helps/enapter +++ b/internal/app/enaptercli/testdata/helps/enapter @@ -8,7 +8,7 @@ DESCRIPTION: Enapter CLI requires access token for authentication. The token can be obtained in your Enapter Cloud account settings. COMMANDS: - devices Manage devices + device Manage devices blueprint Manage blueprints provisioning Create devices of different types rule-engine Manage the rule engine diff --git a/internal/app/enaptercli/testdata/helps/enapter devices b/internal/app/enaptercli/testdata/helps/enapter device similarity index 58% rename from internal/app/enaptercli/testdata/helps/enapter devices rename to internal/app/enaptercli/testdata/helps/enapter device index 56a381b..f92d9a1 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -1,11 +1,11 @@ NAME: - enaptercli.test devices - Manage devices + enaptercli.test device - Manage devices USAGE: - enaptercli.test devices command [command options] + enaptercli.test device command [command options] COMMANDS: - assign_blueprint Assign blueprint to device + assign-blueprint Assign blueprint to device logs Show device logs logsf Follow device logs help, h Shows a list of commands or help for one command diff --git a/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint b/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint similarity index 75% rename from internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint rename to internal/app/enaptercli/testdata/helps/enapter device assign-blueprint index dbf362c..b29b84e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices assign_blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint @@ -1,8 +1,8 @@ NAME: - enaptercli.test devices assign_blueprint - Assign blueprint to device + enaptercli.test device assign-blueprint - Assign blueprint to device USAGE: - enaptercli.test devices assign_blueprint [command options] + enaptercli.test device assign-blueprint [command options] OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter devices logs b/internal/app/enaptercli/testdata/helps/enapter device logs similarity index 90% rename from internal/app/enaptercli/testdata/helps/enapter devices logs rename to internal/app/enaptercli/testdata/helps/enapter device logs index cf9abae..763674f 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices logs +++ b/internal/app/enaptercli/testdata/helps/enapter device logs @@ -1,8 +1,8 @@ NAME: - enaptercli.test devices logs - Show device logs + enaptercli.test device logs - Show device logs USAGE: - enaptercli.test devices logs [command options] + enaptercli.test device logs [command options] OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter devices logsf b/internal/app/enaptercli/testdata/helps/enapter device logsf similarity index 76% rename from internal/app/enaptercli/testdata/helps/enapter devices logsf rename to internal/app/enaptercli/testdata/helps/enapter device logsf index 98a85ed..2b77a87 100644 --- a/internal/app/enaptercli/testdata/helps/enapter devices logsf +++ b/internal/app/enaptercli/testdata/helps/enapter device logsf @@ -1,8 +1,8 @@ NAME: - enaptercli.test devices logsf - Follow device logs + enaptercli.test device logsf - Follow device logs USAGE: - enaptercli.test devices logsf [command options] + enaptercli.test device logsf [command options] OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/cmd.tmpl new file mode 100644 index 0000000..ddc8a25 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/cmd.tmpl @@ -0,0 +1 @@ +enapter3 device assign-blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/out b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/out similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/out rename to internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/out diff --git a/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/req_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/req_0 rename to internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/req_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/resp_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/resp_0 rename to internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/resp_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/cmd.tmpl deleted file mode 100644 index 7e640cb..0000000 --- a/internal/app/enaptercli/testdata/http_req_resp/devices_assign_blueprint/cmd.tmpl +++ /dev/null @@ -1 +0,0 @@ -enapter3 devices assign_blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 From 093b120663ef4da16a35765984a3501c4d3e7eb5 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 30 Dec 2024 06:15:35 +0300 Subject: [PATCH 19/88] feat: add device inspect and list commands --- internal/app/enaptercli/cmd_device.go | 2 + internal/app/enaptercli/cmd_device_inspect.go | 66 +++++++++++++++++++ internal/app/enaptercli/cmd_device_list.go | 60 +++++++++++++++++ .../enaptercli/testdata/helps/enapter device | 2 + .../testdata/helps/enapter device inspect | 16 +++++ .../testdata/helps/enapter device list | 15 +++++ 6 files changed, 161 insertions(+) create mode 100644 internal/app/enaptercli/cmd_device_inspect.go create mode 100644 internal/app/enaptercli/cmd_device_list.go create mode 100644 internal/app/enaptercli/testdata/helps/enapter device inspect create mode 100644 internal/app/enaptercli/testdata/helps/enapter device list diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 0b77941..39f088b 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -20,6 +20,8 @@ func buildCmdDevices() *cli.Command { Name: "device", Usage: "Manage devices", Subcommands: []*cli.Command{ + buildCmdDevicesList(), + buildCmdDevicesInspect(), buildCmdDevicesAssignBlueprint(), buildCmdDevicesLogs(), buildCmdDevicesLogsf(), diff --git a/internal/app/enaptercli/cmd_device_inspect.go b/internal/app/enaptercli/cmd_device_inspect.go new file mode 100644 index 0000000..4074829 --- /dev/null +++ b/internal/app/enaptercli/cmd_device_inspect.go @@ -0,0 +1,66 @@ +package enaptercli + +import ( + "context" + "net/http" + "net/url" + "strings" + + "github.com/urfave/cli/v2" +) + +type cmdDevicesInspect struct { + cmdDevices + deviceID string + expand []string +} + +func buildCmdDevicesInspect() *cli.Command { + cmd := &cmdDevicesInspect{} + return &cli.Command{ + Name: "inspect", + Usage: "Inspect a devices", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdDevicesInspect) Flags() []cli.Flag { + flags := c.cmdDevices.Flags() + return append(flags, &cli.StringFlag{ + Name: "device-id", + Aliases: []string{"d"}, + Usage: "device ID", + Destination: &c.deviceID, + Required: true, + }, &cli.MultiStringFlag{ + Target: &cli.StringSliceFlag{ + Name: "expand", + Usage: "coma separated list of expanded device info", + }, + Destination: &c.expand, + }) +} + +func (c *cmdDevicesInspect) Before(cliCtx *cli.Context) error { + if err := c.cmdDevices.Before(cliCtx); err != nil { + return err + } + return c.validateExpandFlag(cliCtx) +} + +func (c *cmdDevicesInspect) do(ctx context.Context) error { + query := url.Values{} + if len(c.expand) != 0 { + query.Set("expand", strings.Join(c.expand, ",")) + } + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/" + c.deviceID, + Query: query, + }) +} diff --git a/internal/app/enaptercli/cmd_device_list.go b/internal/app/enaptercli/cmd_device_list.go new file mode 100644 index 0000000..57d053a --- /dev/null +++ b/internal/app/enaptercli/cmd_device_list.go @@ -0,0 +1,60 @@ +package enaptercli + +import ( + "context" + "net/http" + "net/url" + "strings" + + "github.com/urfave/cli/v2" +) + +type cmdDevicesList struct { + cmdDevices + expand []string +} + +func buildCmdDevicesList() *cli.Command { + cmd := &cmdDevicesList{} + return &cli.Command{ + Name: "list", + Usage: "List user devices", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdDevicesList) Flags() []cli.Flag { + flags := c.cmdDevices.Flags() + return append(flags, &cli.MultiStringFlag{ + Target: &cli.StringSliceFlag{ + Name: "expand", + Usage: "coma separated list of expanded device info", + }, + Destination: &c.expand, + }) +} + +func (c *cmdDevicesList) Before(cliCtx *cli.Context) error { + if err := c.cmdDevices.Before(cliCtx); err != nil { + return err + } + return c.validateExpandFlag(cliCtx) +} + +func (c *cmdDevicesList) do(ctx context.Context) error { + query := url.Values{} + if len(c.expand) != 0 { + query.Set("expand", strings.Join(c.expand, ",")) + } + + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "", + Query: query, + }) +} diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index f92d9a1..0324760 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -5,6 +5,8 @@ USAGE: enaptercli.test device command [command options] COMMANDS: + list List user devices + inspect Inspect a devices assign-blueprint Assign blueprint to device logs Show device logs logsf Follow device logs diff --git a/internal/app/enaptercli/testdata/helps/enapter device inspect b/internal/app/enaptercli/testdata/helps/enapter device inspect new file mode 100644 index 0000000..f932da4 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter device inspect @@ -0,0 +1,16 @@ +NAME: + enaptercli.test device inspect - Inspect a devices + +USAGE: + enaptercli.test device inspect [command options] + +OPTIONS: + --verbose log extra details about operation (default: false) + --device-id value, -d value device ID + --expand value [ --expand value ] coma separated list of expanded device info + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter device list b/internal/app/enaptercli/testdata/helps/enapter device list new file mode 100644 index 0000000..3758ce0 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter device list @@ -0,0 +1,15 @@ +NAME: + enaptercli.test device list - List user devices + +USAGE: + enaptercli.test device list [command options] + +OPTIONS: + --verbose log extra details about operation (default: false) + --expand value [ --expand value ] coma separated list of expanded device info + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + From 0a6dc6a26dfc671019bd922500f553dc3aadf3d1 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 30 Dec 2024 06:32:05 +0300 Subject: [PATCH 20/88] feat: remove blueprint commands position args --- .../app/enaptercli/cmd_blueprints_inspect.go | 30 ++++++++--------- .../cmd_blueprints_profiles_upload.go | 27 ++++++++------- .../app/enaptercli/cmd_blueprints_upload.go | 33 +++++++++---------- internal/app/enaptercli/errors.go | 3 -- .../testdata/helps/enapter blueprint | 2 +- .../testdata/helps/enapter blueprint inspect | 7 ++-- .../helps/enapter blueprint profiles upload | 7 ++-- .../testdata/helps/enapter blueprint upload | 9 ++--- .../blueprint_inspect_by_id/cmd.tmpl | 2 +- .../blueprint_inspect_by_name/cmd.tmpl | 2 +- 10 files changed, 60 insertions(+), 62 deletions(-) diff --git a/internal/app/enaptercli/cmd_blueprints_inspect.go b/internal/app/enaptercli/cmd_blueprints_inspect.go index 6901281..6e8628f 100644 --- a/internal/app/enaptercli/cmd_blueprints_inspect.go +++ b/internal/app/enaptercli/cmd_blueprints_inspect.go @@ -9,6 +9,7 @@ import ( type cmdBlueprintsInpsect struct { cmdBlueprints + blueprintID string } func buildCmdBlueprintsInspect() *cli.Command { @@ -19,33 +20,32 @@ func buildCmdBlueprintsInspect() *cli.Command { CustomHelpTemplate: cmd.HelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, - Args: true, - ArgsUsage: "", Action: func(cliCtx *cli.Context) error { - return cmd.inspect(cliCtx.Context, cliCtx.Args().Get(0)) + return cmd.inspect(cliCtx.Context) }, } } -func (c *cmdBlueprintsInpsect) Before(cliCtx *cli.Context) error { - if err := c.cmdBlueprints.Before(cliCtx); err != nil { - return err - } - if cliCtx.Args().Get(0) == "" { - return errBlueprintIDMissed - } - return nil +func (c *cmdBlueprintsInpsect) Flags() []cli.Flag { + flags := c.cmdBlueprints.Flags() + return append(flags, &cli.StringFlag{ + Name: "blueprint-id", + Aliases: []string{"b"}, + Usage: "blueprint name or ID to inspect", + Destination: &c.blueprintID, + Required: true, + }) } -func (c *cmdBlueprintsInpsect) inspect(ctx context.Context, blueprintID string) error { - if isBlueprintID(blueprintID) { +func (c *cmdBlueprintsInpsect) inspect(ctx context.Context) error { + if isBlueprintID(c.blueprintID) { return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, - Path: "/blueprints/" + blueprintID, + Path: "/blueprints/" + c.blueprintID, }) } - blueprintName, blueprintTag := parseBlueprintName(blueprintID) + blueprintName, blueprintTag := parseBlueprintName(c.blueprintID) return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, Path: "/blueprints/enapter/" + blueprintName + "/" + blueprintTag, diff --git a/internal/app/enaptercli/cmd_blueprints_profiles_upload.go b/internal/app/enaptercli/cmd_blueprints_profiles_upload.go index 07f5985..2ac7ff0 100644 --- a/internal/app/enaptercli/cmd_blueprints_profiles_upload.go +++ b/internal/app/enaptercli/cmd_blueprints_profiles_upload.go @@ -12,6 +12,7 @@ import ( type cmdBlueprintsProfilesUpload struct { cmdBlueprintsProfiles + profilesPath string } func buildCmdBlueprintsProfilesUpload() *cli.Command { @@ -22,27 +23,25 @@ func buildCmdBlueprintsProfilesUpload() *cli.Command { CustomHelpTemplate: cmd.HelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, - Args: true, - ArgsUsage: "profiles zip file path", Action: func(cliCtx *cli.Context) error { - return cmd.upload(cliCtx.Context, cliCtx.Args().Get(0)) + return cmd.upload(cliCtx.Context) }, } } -func (c *cmdBlueprintsProfilesUpload) Before(cliCtx *cli.Context) error { - if err := c.cmdBlueprintsProfiles.Before(cliCtx); err != nil { - return err - } - - if cliCtx.Args().Get(0) == "" { - return errProfilesPathMissed - } - return nil +func (c *cmdBlueprintsProfilesUpload) Flags() []cli.Flag { + flags := c.cmdBlueprintsProfiles.Flags() + return append(flags, &cli.StringFlag{ + Name: "profiles-path", + Aliases: []string{"p"}, + Usage: "profiles zip file path", + Destination: &c.profilesPath, + Required: true, + }) } -func (c *cmdBlueprintsProfilesUpload) upload(ctx context.Context, blueprintPath string) error { - data, err := os.ReadFile(blueprintPath) +func (c *cmdBlueprintsProfilesUpload) upload(ctx context.Context) error { + data, err := os.ReadFile(c.profilesPath) if err != nil { return fmt.Errorf("read zip file: %w", err) } diff --git a/internal/app/enaptercli/cmd_blueprints_upload.go b/internal/app/enaptercli/cmd_blueprints_upload.go index a9d1ce2..683160d 100644 --- a/internal/app/enaptercli/cmd_blueprints_upload.go +++ b/internal/app/enaptercli/cmd_blueprints_upload.go @@ -12,49 +12,48 @@ import ( type cmdBlueprintsUpload struct { cmdBlueprints + blueprintPath string } func buildCmdBlueprintsUpload() *cli.Command { cmd := &cmdBlueprintsUpload{} return &cli.Command{ Name: "upload", - Usage: "Upload blueprint directory into Platform", + Usage: "Upload blueprint into Platform", CustomHelpTemplate: cmd.HelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, - Args: true, - ArgsUsage: "", Action: func(cliCtx *cli.Context) error { - return cmd.upload(cliCtx.Context, cliCtx.Args().Get(0)) + return cmd.upload(cliCtx.Context) }, } } -func (c *cmdBlueprintsUpload) Before(cliCtx *cli.Context) error { - if err := c.cmdBlueprints.Before(cliCtx); err != nil { - return err - } - - if cliCtx.Args().Get(0) == "" { - return errBlueprintPathMissed - } - return nil +func (c *cmdBlueprintsUpload) Flags() []cli.Flag { + flags := c.cmdBlueprints.Flags() + return append(flags, &cli.StringFlag{ + Name: "path", + Aliases: []string{"p"}, + Usage: "blueprint path (zip file or directory)", + Destination: &c.blueprintPath, + Required: true, + }) } -func (c *cmdBlueprintsUpload) upload(ctx context.Context, blueprintPath string) error { - fi, err := os.Stat(blueprintPath) +func (c *cmdBlueprintsUpload) upload(ctx context.Context) error { + fi, err := os.Stat(c.blueprintPath) if err != nil { return fmt.Errorf("check blueprint path: %w", err) } var data []byte if fi.IsDir() { - data, err = zipDir(blueprintPath) + data, err = zipDir(c.blueprintPath) if err != nil { return fmt.Errorf("zip blueprint directory: %w", err) } } else { - data, err = os.ReadFile(blueprintPath) + data, err = os.ReadFile(c.blueprintPath) if err != nil { return fmt.Errorf("read blueprint zip file: %w", err) } diff --git a/internal/app/enaptercli/errors.go b/internal/app/enaptercli/errors.go index 63f31be..7f05d22 100644 --- a/internal/app/enaptercli/errors.go +++ b/internal/app/enaptercli/errors.go @@ -5,8 +5,5 @@ import "errors" var ( errAPITokenMissed = errors.New("API token missing. Set it up using environment " + "variable ENAPTER3_API_TOKEN") - errBlueprintIDMissed = errors.New("blueprint ID is missed") - errBlueprintPathMissed = errors.New("blueprint path is missed") errUnsupportedFlagValue = errors.New("unsupported flag value") - errProfilesPathMissed = errors.New("profiles path is missed") ) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint b/internal/app/enaptercli/testdata/helps/enapter blueprint index 0260052..367e380 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint @@ -6,7 +6,7 @@ USAGE: COMMANDS: profiles Manage blueprints profiles - upload Upload blueprint directory into Platform + upload Upload blueprint into Platform download Download blueprint zip from Platform inspect Get blueprint metainfo help, h Shows a list of commands or help for one command diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint inspect b/internal/app/enaptercli/testdata/helps/enapter blueprint inspect index f0b0dbd..2309ca4 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint inspect +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint inspect @@ -2,11 +2,12 @@ NAME: enaptercli.test blueprint inspect - Get blueprint metainfo USAGE: - enaptercli.test blueprint inspect [command options] + enaptercli.test blueprint inspect [command options] OPTIONS: - --verbose log extra details about operation (default: false) - --help, -h show help + --verbose log extra details about operation (default: false) + --blueprint-id value, -b value blueprint name or ID to inspect + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload index 5c33dbe..598c1a2 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload @@ -2,11 +2,12 @@ NAME: enaptercli.test blueprint profiles upload - Upload profiles into Platform USAGE: - enaptercli.test blueprint profiles upload [command options] profiles zip file path + enaptercli.test blueprint profiles upload [command options] OPTIONS: - --verbose log extra details about operation (default: false) - --help, -h show help + --verbose log extra details about operation (default: false) + --profiles-path value, -p value profiles zip file path + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint upload b/internal/app/enaptercli/testdata/helps/enapter blueprint upload index 6c88199..0e7e170 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint upload @@ -1,12 +1,13 @@ NAME: - enaptercli.test blueprint upload - Upload blueprint directory into Platform + enaptercli.test blueprint upload - Upload blueprint into Platform USAGE: - enaptercli.test blueprint upload [command options] + enaptercli.test blueprint upload [command options] OPTIONS: - --verbose log extra details about operation (default: false) - --help, -h show help + --verbose log extra details about operation (default: false) + --path value, -p value blueprint path (zip file or directory) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl index b30e692..1c4cc59 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl @@ -1 +1 @@ -enapter3 blueprint inspect {{.BaseFlags}} cdd82438-dda8-4f69-aad1-0be9adeab964 +enapter3 blueprint inspect {{.BaseFlags}} --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl index b0388af..5b9d1a7 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl @@ -1 +1 @@ -enapter3 blueprint inspect {{.BaseFlags}} test_blueprint_name +enapter3 blueprint inspect {{.BaseFlags}} --blueprint-id test_blueprint_name From f0e1d42bed4192a041300b215a4aa85ead4d678a Mon Sep 17 00:00:00 2001 From: Roman Novatorov Date: Mon, 6 Jan 2025 12:13:10 +0100 Subject: [PATCH 21/88] fix: remove redundant print --- internal/app/enaptercli/cmd_rule_engine_rule_disable.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_disable.go b/internal/app/enaptercli/cmd_rule_engine_rule_disable.go index 3a30258..f4ff79f 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_disable.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_disable.go @@ -49,8 +49,6 @@ func (c *cmdRuleEngineRuleDisable) do(ctx context.Context) error { if err != nil { return fmt.Errorf("build request: %w", err) } - - fmt.Println(c.ruleIDs) return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodPost, Path: "/batch_disable", From b8621314d08cae9c4997da945fa66545903ef70a Mon Sep 17 00:00:00 2001 From: Tatyana Date: Tue, 7 Jan 2025 15:13:17 +0100 Subject: [PATCH 22/88] feat: remove name field from rule --- internal/app/enaptercli/cmd_rule_engine_rule_create.go | 7 ------- internal/app/enaptercli/cmd_rule_engine_rule_update.go | 10 ---------- .../testdata/helps/enapter rule-engine rule create | 1 - .../testdata/helps/enapter rule-engine rule update | 1 - 4 files changed, 19 deletions(-) diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_create.go b/internal/app/enaptercli/cmd_rule_engine_rule_create.go index 69a5a15..d0fec23 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_create.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_create.go @@ -16,7 +16,6 @@ import ( type cmdRuleEngineRuleCreate struct { cmdRuleEngineRule slug string - name string scriptPath string runtimeVersion int execInterval time.Duration @@ -43,11 +42,6 @@ func (c *cmdRuleEngineRuleCreate) Flags() []cli.Flag { Name: "slug", Usage: "Slug of a new rule", Destination: &c.slug, - }, - &cli.StringFlag{ - Name: "name", - Usage: "Name of a new rule", - Destination: &c.name, Required: true, }, &cli.StringFlag{ @@ -87,7 +81,6 @@ func (c *cmdRuleEngineRuleCreate) do(ctx context.Context) error { body, err := json.Marshal(map[string]any{ "rule": map[string]any{ "slug": c.slug, - "name": c.name, "script": map[string]any{ "code": base64.StdEncoding.EncodeToString(scriptBytes), "runtime_version": c.runtimeVersion, diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update.go b/internal/app/enaptercli/cmd_rule_engine_rule_update.go index 74a63d9..ad46cc7 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_update.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update.go @@ -14,7 +14,6 @@ type cmdRuleEngineRuleUpdate struct { cmdRuleEngineRule ruleID string slug string - name string } func buildCmdRuleEngineRuleUpdate() *cli.Command { @@ -44,11 +43,6 @@ func (c *cmdRuleEngineRuleUpdate) Flags() []cli.Flag { Usage: "A new rule slug", Destination: &c.slug, }, - &cli.StringFlag{ - Name: "name", - Usage: "A new rule name", - Destination: &c.name, - }, ) } @@ -65,10 +59,6 @@ func (c *cmdRuleEngineRuleUpdate) do(cliCtx *cli.Context) error { payload.Rule["slug"] = c.slug payload.UpdateMask += "slug," } - if cliCtx.IsSet("name") { - payload.Rule["name"] = c.name - payload.UpdateMask += "name," - } payload.UpdateMask = strings.TrimSuffix(payload.UpdateMask, ",") diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create index d5448cf..380415b 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create @@ -7,7 +7,6 @@ USAGE: OPTIONS: --verbose log extra details about operation (default: false) --slug value Slug of a new rule - --name value Name of a new rule --script value Path to a file containing the script code --runtime-version value Version of a runtime to use for the script execution (default: 3) --exec-interval value How often to execute the script. This option is only compatible with the runtime version 1 (default: 0s) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update index 10297ab..100a633 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update @@ -8,7 +8,6 @@ OPTIONS: --verbose log extra details about operation (default: false) --rule-id value Rule ID or slug to update --slug value A new rule slug - --name value A new rule name --help, -h show help ENVIRONMENT VARIABLES: From 7ea959a1ca3e4b397e119f9309f034d5ae616a0b Mon Sep 17 00:00:00 2001 From: Roman Novatorov Date: Fri, 10 Jan 2025 20:58:08 +0100 Subject: [PATCH 23/88] rule-engine: fix create and update commands --- .../app/enaptercli/cmd_rule_engine_rule_create.go | 14 ++++++-------- .../app/enaptercli/cmd_rule_engine_rule_update.go | 14 ++------------ 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_create.go b/internal/app/enaptercli/cmd_rule_engine_rule_create.go index d0fec23..df566b4 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_create.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_create.go @@ -79,15 +79,13 @@ func (c *cmdRuleEngineRuleCreate) do(ctx context.Context) error { } body, err := json.Marshal(map[string]any{ - "rule": map[string]any{ - "slug": c.slug, - "script": map[string]any{ - "code": base64.StdEncoding.EncodeToString(scriptBytes), - "runtime_version": c.runtimeVersion, - "exec_interval": c.execInterval.String(), - }, + "slug": c.slug, + "script": map[string]any{ + "code": base64.StdEncoding.EncodeToString(scriptBytes), + "runtime_version": c.runtimeVersion, + "exec_interval": c.execInterval.String(), }, - "disable_rule": c.disable, + "disable": c.disable, }) if err != nil { return fmt.Errorf("build request: %w", err) diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update.go b/internal/app/enaptercli/cmd_rule_engine_rule_update.go index ad46cc7..405b114 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_update.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "net/http" - "strings" "github.com/urfave/cli/v2" ) @@ -47,21 +46,12 @@ func (c *cmdRuleEngineRuleUpdate) Flags() []cli.Flag { } func (c *cmdRuleEngineRuleUpdate) do(cliCtx *cli.Context) error { - payload := struct { - Rule map[string]any `json:"rule"` - UpdateMask string `json:"update_mask"` - }{ - Rule: make(map[string]any), - UpdateMask: "", - } + payload := make(map[string]any) if cliCtx.IsSet("slug") { - payload.Rule["slug"] = c.slug - payload.UpdateMask += "slug," + payload["slug"] = c.slug } - payload.UpdateMask = strings.TrimSuffix(payload.UpdateMask, ",") - body, err := json.Marshal(payload) if err != nil { return fmt.Errorf("build request: %w", err) From f1228d586b34f342c9f48bd6b3c6eedc7303ba9a Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Mon, 13 Jan 2025 18:27:19 +0400 Subject: [PATCH 24/88] feat: support streaming of rule logs --- go.mod | 1 + go.sum | 2 + internal/app/enaptercli/cmd_base.go | 33 +++++++ .../app/enaptercli/cmd_rule_engine_rule.go | 1 + .../enaptercli/cmd_rule_engine_rule_logs.go | 87 +++++++++++++++++++ .../testdata/helps/enapter rule-engine rule | 1 + .../helps/enapter rule-engine rule logs | 16 ++++ 7 files changed, 141 insertions(+) create mode 100644 internal/app/enaptercli/cmd_rule_engine_rule_logs.go create mode 100644 internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs diff --git a/go.mod b/go.mod index 0766ab4..519abbb 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/enapter/enapter-cli go 1.21 require ( + github.com/gorilla/websocket v1.5.3 github.com/stretchr/testify v1.9.0 github.com/urfave/cli/v2 v2.27.4 ) diff --git a/go.sum b/go.sum index b5436ad..d960876 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 529a0b2..130b0cc 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -12,6 +12,7 @@ import ( "net/url" "strings" + "github.com/gorilla/websocket" "github.com/urfave/cli/v2" ) @@ -122,6 +123,38 @@ func (c *cmdBase) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) erro return p.RespProcessor(resp) } +func (c *cmdBase) dialWebSocket(ctx context.Context, path string) (*websocket.Conn, error) { + url, err := url.Parse(c.apiHost + "/v3" + path) + if err != nil { + return nil, fmt.Errorf("parse url: %w", err) + } + + switch url.Scheme { + case "https": + url.Scheme = "wss" + case "http": + url.Scheme = "ws" + } + + headers := make(http.Header) + headers.Add("X-Enapter-Auth-Token", c.token) + + if c.verbose { + fmt.Fprintf(c.writer, "== Dialing WebSocket at %s\n", url.String()) + } + + //nolint:bodyclose // body should be closed by callers + conn, resp, err := websocket.DefaultDialer.DialContext(ctx, url.String(), headers) + if err != nil { + return nil, fmt.Errorf("dial: %w", err) + } + if resp.StatusCode != http.StatusSwitchingProtocols { + return nil, cli.Exit(parseRespErrorMessage(resp), 1) + } + + return conn, nil +} + func (c *cmdBase) defaultRespProcessor(resp *http.Response) error { if resp.StatusCode != http.StatusOK { return cli.Exit(parseRespErrorMessage(resp), 1) diff --git a/internal/app/enaptercli/cmd_rule_engine_rule.go b/internal/app/enaptercli/cmd_rule_engine_rule.go index 9fb536e..6b339ba 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule.go @@ -30,6 +30,7 @@ func buildCmdRuleEngineRule() *cli.Command { buildCmdRuleEngineRuleList(), buildCmdRuleEngineRuleUpdate(), buildCmdRuleEngineRuleUpdateScript(), + buildCmdRuleEngineRuleLogs(), }, } } diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_logs.go b/internal/app/enaptercli/cmd_rule_engine_rule_logs.go new file mode 100644 index 0000000..0df293d --- /dev/null +++ b/internal/app/enaptercli/cmd_rule_engine_rule_logs.go @@ -0,0 +1,87 @@ +package enaptercli + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/urfave/cli/v2" +) + +type cmdRuleEngineRuleLogs struct { + cmdRuleEngineRule + ruleID string + follow bool +} + +func buildCmdRuleEngineRuleLogs() *cli.Command { + cmd := &cmdRuleEngineRuleLogs{} + return &cli.Command{ + Name: "logs", + Usage: "Show rule logs", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx) + }, + } +} + +func (c *cmdRuleEngineRuleLogs) Flags() []cli.Flag { + return append(c.cmdRuleEngineRule.Flags(), + &cli.StringFlag{ + Name: "rule-id", + Usage: "rule ID", + Destination: &c.ruleID, + Required: true, + }, + &cli.BoolFlag{ + Name: "follow", + Aliases: []string{"f"}, + Usage: "follow log output", + Destination: &c.follow, + }, + ) +} + +func (c *cmdRuleEngineRuleLogs) do(cliCtx *cli.Context) error { + if !c.follow { + return cli.Exit("Currently, only follow mode (--follow) is supported.", 1) + } + + path := fmt.Sprintf("/site/rule_engine/rules/%s/logs/ws", c.ruleID) + conn, err := c.dialWebSocket(cliCtx.Context, path) + if err != nil { + return fmt.Errorf("connect: %w", err) + } + + go func() { + <-cliCtx.Done() + conn.Close() + }() + + for { + _, r, err := conn.NextReader() + if err != nil { + select { + case <-cliCtx.Done(): + return nil + default: + return fmt.Errorf("read: %w", err) + } + } + + var msg struct { + Timestamp int64 `json:"timestamp"` + Severity string `json:"severity"` + Message string `json:"message"` + } + if err := json.NewDecoder(r).Decode(&msg); err != nil { + return fmt.Errorf("parse payload: %w", err) + } + + ts := time.Unix(msg.Timestamp, 0).Format(time.RFC3339) + fmt.Fprintf(c.writer, "%s [%s] %s\n", ts, msg.Severity, msg.Message) + } +} diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule index f8f1aeb..e37f204 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule @@ -13,6 +13,7 @@ COMMANDS: list List rules update Update a rule update-script Update the script of a rule + logs Show rule logs help, h Shows a list of commands or help for one command OPTIONS: diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs new file mode 100644 index 0000000..f058d75 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs @@ -0,0 +1,16 @@ +NAME: + enaptercli.test rule-engine rule logs - Show rule logs + +USAGE: + enaptercli.test rule-engine rule logs [command options] + +OPTIONS: + --verbose log extra details about operation (default: false) + --rule-id value rule ID + --follow, -f follow log output (default: false) + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + From d1de38f037339ca17f9e370c52d62d5a8984b937 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 13 Jan 2025 23:39:24 +0300 Subject: [PATCH 25/88] feat: provision lua device with a slug --- internal/app/enaptercli/cmd_provisioning_lua_device.go | 6 ++++++ .../testdata/helps/enapter provisioning lua-device | 1 + 2 files changed, 7 insertions(+) diff --git a/internal/app/enaptercli/cmd_provisioning_lua_device.go b/internal/app/enaptercli/cmd_provisioning_lua_device.go index dc7c64e..2935041 100644 --- a/internal/app/enaptercli/cmd_provisioning_lua_device.go +++ b/internal/app/enaptercli/cmd_provisioning_lua_device.go @@ -13,6 +13,7 @@ import ( type cmdProvisioningLua struct { cmdProvisioning deviceName string + deviceSlug string runtimeID string blueprintID string } @@ -45,6 +46,10 @@ func (c *cmdProvisioningLua) Flags() []cli.Flag { Usage: "name of a new Lua device", Destination: &c.deviceName, Required: true, + }, &cli.StringFlag{ + Name: "device-slug", + Usage: "slug of a new Lua device", + Destination: &c.deviceSlug, }, &cli.StringFlag{ Name: "blueprint-id", Aliases: []string{"b"}, @@ -58,6 +63,7 @@ func (c *cmdProvisioningLua) do(ctx context.Context) error { body, err := json.Marshal(map[string]interface{}{ "runtime_id": c.runtimeID, "name": c.deviceName, + "slug": c.deviceSlug, "blueprint_id": c.blueprintID, }) if err != nil { diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device b/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device index c690477..a0ad5ed 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device @@ -8,6 +8,7 @@ OPTIONS: --verbose log extra details about operation (default: false) --runtime-id value, -r value runtime UCM device ID where to run a new Lua device --device-name value, -n value name of a new Lua device + --device-slug value slug of a new Lua device --blueprint-id value, -b value blueprint ID of a new Lua device --help, -h show help From a7e00d8325ba36273ee65a52934e93179f1a23c4 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Wed, 15 Jan 2025 14:59:32 +0400 Subject: [PATCH 26/88] feat: add reconnections for rule logs streaming --- internal/app/enaptercli/cmd_base.go | 8 ++- .../enaptercli/cmd_rule_engine_rule_logs.go | 50 ++++++++++++++----- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 130b0cc..4c21594 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -11,6 +11,7 @@ import ( "net/http" "net/url" "strings" + "time" "github.com/gorilla/websocket" "github.com/urfave/cli/v2" @@ -143,8 +144,13 @@ func (c *cmdBase) dialWebSocket(ctx context.Context, path string) (*websocket.Co fmt.Fprintf(c.writer, "== Dialing WebSocket at %s\n", url.String()) } + const timeout = 5 * time.Second + dialer := websocket.Dialer{ + HandshakeTimeout: timeout, + } + //nolint:bodyclose // body should be closed by callers - conn, resp, err := websocket.DefaultDialer.DialContext(ctx, url.String(), headers) + conn, resp, err := dialer.DialContext(ctx, url.String(), headers) if err != nil { return nil, fmt.Errorf("dial: %w", err) } diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_logs.go b/internal/app/enaptercli/cmd_rule_engine_rule_logs.go index 0df293d..0262eb6 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_logs.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_logs.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/gorilla/websocket" "github.com/urfave/cli/v2" ) @@ -51,25 +52,50 @@ func (c *cmdRuleEngineRuleLogs) do(cliCtx *cli.Context) error { } path := fmt.Sprintf("/site/rule_engine/rules/%s/logs/ws", c.ruleID) - conn, err := c.dialWebSocket(cliCtx.Context, path) - if err != nil { - return fmt.Errorf("connect: %w", err) - } - - go func() { - <-cliCtx.Done() - conn.Close() - }() + for retry := false; ; retry = true { + if retry { + fmt.Fprintln(c.writer, "Reconnecting...") + time.Sleep(time.Second) + } - for { - _, r, err := conn.NextReader() + conn, err := c.dialWebSocket(cliCtx.Context, path) if err != nil { select { case <-cliCtx.Done(): return nil default: - return fmt.Errorf("read: %w", err) + fmt.Fprintln(c.writer, "Failed to retrieve logs:", err) + continue + } + } + fmt.Fprintln(c.writer, "Connection established") + + closeCh := make(chan struct{}) + go func() { + select { + case <-cliCtx.Done(): + case <-closeCh: } + conn.Close() + }() + + if err := c.runProxy(conn); err != nil { + select { + case <-cliCtx.Done(): + return nil + default: + fmt.Fprintln(c.writer, "Failed to retrieve logs:", err) + close(closeCh) + } + } + } +} + +func (c *cmdRuleEngineRuleLogs) runProxy(conn *websocket.Conn) error { + for { + _, r, err := conn.NextReader() + if err != nil { + return fmt.Errorf("read: %w", err) } var msg struct { From 9c49ada30261a2d7492f729307562cb11a0eca0c Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 18 Feb 2025 01:27:07 +0300 Subject: [PATCH 27/88] bug: get nil req body in verbose mode --- internal/app/enaptercli/cmd_base.go | 35 +++++++++++++++++++---------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 4c21594..6eb74a3 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -94,18 +94,9 @@ func (c *cmdBase) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) erro req.URL.RawQuery = p.Query.Encode() if c.verbose { - bb := &bytes.Buffer{} - if _, err := io.Copy(bb, req.Body); err != nil { - return fmt.Errorf("reading body for verbose log: %w", err) - } - if err := req.Body.Close(); err != nil { - return fmt.Errorf("closing body for verbose log: %w", err) - } - req.Body = io.NopCloser(bb) - - bodyStr := bb.String() - if p.ContentType != contentTypeJSON { - bodyStr = base64.RawStdEncoding.EncodeToString(bb.Bytes()) + bodyStr, err := getRequestBodyString(req, p.ContentType) + if err != nil { + return err } fmt.Fprintf(c.writer, "== Do http request %s %s\n", p.Method, req.URL.String()) @@ -174,6 +165,26 @@ func (c *cmdBase) defaultRespProcessor(resp *http.Response) error { return nil } +func getRequestBodyString(req *http.Request, contentType string) (string, error) { + if req.Body == nil { + return "", nil + } + bb := &bytes.Buffer{} + if _, err := io.Copy(bb, req.Body); err != nil { + return "", fmt.Errorf("reading body for verbose log: %w", err) + } + if err := req.Body.Close(); err != nil { + return "", fmt.Errorf("closing body for verbose log: %w", err) + } + req.Body = io.NopCloser(bb) + + if contentType != contentTypeJSON { + return base64.RawStdEncoding.EncodeToString(bb.Bytes()), nil + } + + return bb.String(), nil +} + func okRespBodyProcessor(fn func(body io.Reader) error) func(resp *http.Response) error { return func(resp *http.Response) error { if resp.StatusCode != http.StatusOK { From 4133022e4e474d8e7e87bfb4527be5667b3e3943 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 18 Feb 2025 01:30:14 +0300 Subject: [PATCH 28/88] feat: add device delete command --- internal/app/enaptercli/cmd_device.go | 1 + internal/app/enaptercli/cmd_device_delete.go | 46 +++++++++++++++++++ .../enaptercli/testdata/helps/enapter device | 1 + .../testdata/helps/enapter device delete | 15 ++++++ 4 files changed, 63 insertions(+) create mode 100644 internal/app/enaptercli/cmd_device_delete.go create mode 100644 internal/app/enaptercli/testdata/helps/enapter device delete diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 39f088b..322523e 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -25,6 +25,7 @@ func buildCmdDevices() *cli.Command { buildCmdDevicesAssignBlueprint(), buildCmdDevicesLogs(), buildCmdDevicesLogsf(), + buildCmdDevicesDelete(), }, } } diff --git a/internal/app/enaptercli/cmd_device_delete.go b/internal/app/enaptercli/cmd_device_delete.go new file mode 100644 index 0000000..8338136 --- /dev/null +++ b/internal/app/enaptercli/cmd_device_delete.go @@ -0,0 +1,46 @@ +package enaptercli + +import ( + "context" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdDevicesDelete struct { + cmdDevices + deviceID string +} + +func buildCmdDevicesDelete() *cli.Command { + cmd := &cmdDevicesDelete{} + return &cli.Command{ + Name: "delete", + Usage: "Delete a device", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdDevicesDelete) Flags() []cli.Flag { + flags := c.cmdDevices.Flags() + return append(flags, + &cli.StringFlag{ + Name: "device-id", + Usage: "device ID", + Destination: &c.deviceID, + Required: true, + }, + ) +} + +func (c *cmdDevicesDelete) do(ctx context.Context) error { + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodDelete, + Path: "/" + c.deviceID, + }) +} diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index 0324760..6cf2dbf 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -10,6 +10,7 @@ COMMANDS: assign-blueprint Assign blueprint to device logs Show device logs logsf Follow device logs + delete Delete a device help, h Shows a list of commands or help for one command OPTIONS: diff --git a/internal/app/enaptercli/testdata/helps/enapter device delete b/internal/app/enaptercli/testdata/helps/enapter device delete new file mode 100644 index 0000000..da762be --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter device delete @@ -0,0 +1,15 @@ +NAME: + enaptercli.test device delete - Delete a device + +USAGE: + enaptercli.test device delete [command options] + +OPTIONS: + --verbose log extra details about operation (default: false) + --device-id value device ID + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + From 4303049bf58c10a106d0e9e696486286f6439fa9 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 21 Feb 2025 14:06:02 +0300 Subject: [PATCH 29/88] feat: support device command executions --- internal/app/enaptercli/cmd_base.go | 13 +++ internal/app/enaptercli/cmd_device.go | 11 +-- .../enaptercli/cmd_device_execute_command.go | 83 +++++++++++++++++++ .../app/enaptercli/cmd_device_execution.go | 47 +++++++++++ .../cmd_device_execution_inspect.go | 67 +++++++++++++++ .../enaptercli/cmd_device_execution_list.go | 60 ++++++++++++++ .../enaptercli/testdata/helps/enapter device | 2 + .../helps/enapter device execute-command | 18 ++++ .../testdata/helps/enapter device execution | 13 +++ .../helps/enapter device execution inspect | 17 ++++ .../helps/enapter device execution list | 16 ++++ 11 files changed, 339 insertions(+), 8 deletions(-) create mode 100644 internal/app/enaptercli/cmd_device_execute_command.go create mode 100644 internal/app/enaptercli/cmd_device_execution.go create mode 100644 internal/app/enaptercli/cmd_device_execution_inspect.go create mode 100644 internal/app/enaptercli/cmd_device_execution_list.go create mode 100644 internal/app/enaptercli/testdata/helps/enapter device execute-command create mode 100644 internal/app/enaptercli/testdata/helps/enapter device execution create mode 100644 internal/app/enaptercli/testdata/helps/enapter device execution inspect create mode 100644 internal/app/enaptercli/testdata/helps/enapter device execution list diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 6eb74a3..dcafb16 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -10,6 +10,7 @@ import ( "io" "net/http" "net/url" + "slices" "strings" "time" @@ -215,3 +216,15 @@ func parseRespErrorMessage(resp *http.Response) string { return fmt.Sprintf("request finished with HTTP status %q, but without error message", resp.Status) } + +func validateExpandFlag(cliCtx *cli.Context, supportedFields []string) error { + slices.Sort(supportedFields) + + for _, field := range cliCtx.StringSlice("expand") { + if _, ok := slices.BinarySearch(supportedFields, field); !ok { + return fmt.Errorf("%w: %s is not supported by expand, should be one of %s", + errUnsupportedFlagValue, field, supportedFields) + } + } + return nil +} diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 322523e..17466a9 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -26,6 +26,8 @@ func buildCmdDevices() *cli.Command { buildCmdDevicesLogs(), buildCmdDevicesLogsf(), buildCmdDevicesDelete(), + buildCmdDevicesExecuteCommand(), + buildCmdDeviceExecution(), }, } } @@ -60,12 +62,5 @@ func (c *cmdDevices) parseAndDumpDeviceLogs(body io.Reader) (int, error) { func (c *cmdDevices) validateExpandFlag(cliCtx *cli.Context) error { supportedFields := []string{"connectivity", "manifest", "properties", "communication_info", "site"} slices.Sort(supportedFields) - - for _, field := range cliCtx.StringSlice("expand") { - if _, ok := slices.BinarySearch(supportedFields, field); !ok { - return fmt.Errorf("%w: %s is not supported by expand, should be one of %s", - errUnsupportedFlagValue, field, supportedFields) - } - } - return nil + return validateExpandFlag(cliCtx, supportedFields) } diff --git a/internal/app/enaptercli/cmd_device_execute_command.go b/internal/app/enaptercli/cmd_device_execute_command.go new file mode 100644 index 0000000..b7d7069 --- /dev/null +++ b/internal/app/enaptercli/cmd_device_execute_command.go @@ -0,0 +1,83 @@ +package enaptercli + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdDevicesExecuteCommand struct { + cmdDevices + deviceID string + cmdName string + cmdArgs string + ephemeral bool +} + +func buildCmdDevicesExecuteCommand() *cli.Command { + cmd := &cmdDevicesExecuteCommand{} + return &cli.Command{ + Name: "execute-command", + Usage: "Execute a device command", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdDevicesExecuteCommand) Flags() []cli.Flag { + flags := c.cmdDevices.Flags() + return append(flags, + &cli.StringFlag{ + Name: "device-id", + Usage: "device ID", + Destination: &c.deviceID, + Required: true, + }, + &cli.StringFlag{ + Name: "cmd-name", + Usage: "command name", + Destination: &c.cmdName, + Required: true, + }, + &cli.StringFlag{ + Name: "cmd-args", + Usage: "command args (should be a JSON string)", + Destination: &c.cmdArgs, + }, + &cli.BoolFlag{ + Name: "ephemeral", + Usage: "run command in ephemeral mode", + Destination: &c.ephemeral, + }, + ) +} + +func (c *cmdDevicesExecuteCommand) do(ctx context.Context) error { + reqBody := struct { + Name string `json:"name"` + Args json.RawMessage `json:"arguments,omitempty"` + Ephemeral bool `json:"ephemeral,omitempty"` + }{ + Name: c.cmdName, + Args: json.RawMessage(c.cmdArgs), + Ephemeral: c.ephemeral, + } + data, err := json.Marshal(reqBody) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/" + c.deviceID + "/execute_command", + Body: bytes.NewReader(data), + }) +} diff --git a/internal/app/enaptercli/cmd_device_execution.go b/internal/app/enaptercli/cmd_device_execution.go new file mode 100644 index 0000000..f4bd38a --- /dev/null +++ b/internal/app/enaptercli/cmd_device_execution.go @@ -0,0 +1,47 @@ +package enaptercli + +import ( + "context" + "fmt" + "net/url" + + "github.com/urfave/cli/v2" +) + +type cmdDeviceExecution struct { + cmdDevices + deviceID string +} + +func buildCmdDeviceExecution() *cli.Command { + return &cli.Command{ + Name: "execution", + Usage: "Manage device command executions", + Subcommands: []*cli.Command{ + buildCmdDeviceExecutionList(), + buildCmdDeviceExecutionInspect(), + }, + } +} + +func (c *cmdDeviceExecution) Flags() []cli.Flag { + flags := c.cmdDevices.Flags() + return append(flags, + &cli.StringFlag{ + Name: "device-id", + Aliases: []string{"d"}, + Usage: "device ID", + Destination: &c.deviceID, + Required: true, + }, + ) +} + +func (c *cmdDeviceExecution) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { + path, err := url.JoinPath(c.deviceID, "command_executions", p.Path) + if err != nil { + return fmt.Errorf("join path: %w", err) + } + p.Path = path + return c.cmdDevices.doHTTPRequest(ctx, p) +} diff --git a/internal/app/enaptercli/cmd_device_execution_inspect.go b/internal/app/enaptercli/cmd_device_execution_inspect.go new file mode 100644 index 0000000..04ff3fb --- /dev/null +++ b/internal/app/enaptercli/cmd_device_execution_inspect.go @@ -0,0 +1,67 @@ +package enaptercli + +import ( + "context" + "net/http" + "net/url" + "strings" + + "github.com/urfave/cli/v2" +) + +type cmdDeviceExecutionInspect struct { + cmdDeviceExecution + executionID string + expand []string +} + +func buildCmdDeviceExecutionInspect() *cli.Command { + cmd := &cmdDeviceExecutionInspect{} + return &cli.Command{ + Name: "inspect", + Usage: "Inspect a device command execution", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdDeviceExecutionInspect) Flags() []cli.Flag { + flags := c.cmdDeviceExecution.Flags() + return append(flags, + &cli.StringFlag{ + Name: "execution-id", + Usage: "execution ID", + Destination: &c.executionID, + Required: true, + }, &cli.MultiStringFlag{ + Target: &cli.StringSliceFlag{ + Name: "expand", + Usage: "coma separated list of expanded options", + }, + Destination: &c.expand, + }, + ) +} + +func (c *cmdDeviceExecutionInspect) Before(cliCtx *cli.Context) error { + if err := c.cmdDevices.Before(cliCtx); err != nil { + return err + } + return validateExpandFlag(cliCtx, []string{"log"}) +} + +func (c *cmdDeviceExecutionInspect) do(ctx context.Context) error { + query := url.Values{} + if len(c.expand) != 0 { + query.Set("expand", strings.Join(c.expand, ",")) + } + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/" + c.executionID, + Query: query, + }) +} diff --git a/internal/app/enaptercli/cmd_device_execution_list.go b/internal/app/enaptercli/cmd_device_execution_list.go new file mode 100644 index 0000000..be2203f --- /dev/null +++ b/internal/app/enaptercli/cmd_device_execution_list.go @@ -0,0 +1,60 @@ +package enaptercli + +import ( + "context" + "net/http" + "net/url" + "strings" + + "github.com/urfave/cli/v2" +) + +type cmdDeviceExecutionList struct { + cmdDeviceExecution + expand []string +} + +func buildCmdDeviceExecutionList() *cli.Command { + cmd := &cmdDeviceExecutionList{} + return &cli.Command{ + Name: "list", + Usage: "List device command executions", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdDeviceExecutionList) Flags() []cli.Flag { + flags := c.cmdDeviceExecution.Flags() + return append(flags, + &cli.MultiStringFlag{ + Target: &cli.StringSliceFlag{ + Name: "expand", + Usage: "coma separated list of expanded options", + }, + Destination: &c.expand, + }, + ) +} + +func (c *cmdDeviceExecutionList) Before(cliCtx *cli.Context) error { + if err := c.cmdDevices.Before(cliCtx); err != nil { + return err + } + return validateExpandFlag(cliCtx, []string{"ephemeral"}) +} + +func (c *cmdDeviceExecutionList) do(ctx context.Context) error { + query := url.Values{} + if len(c.expand) != 0 { + query.Set("expand", strings.Join(c.expand, ",")) + } + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Query: query, + }) +} diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index 6cf2dbf..df05735 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -11,6 +11,8 @@ COMMANDS: logs Show device logs logsf Follow device logs delete Delete a device + execute-command Execute a device command + execution Manage device command executions help, h Shows a list of commands or help for one command OPTIONS: diff --git a/internal/app/enaptercli/testdata/helps/enapter device execute-command b/internal/app/enaptercli/testdata/helps/enapter device execute-command new file mode 100644 index 0000000..a808cc6 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter device execute-command @@ -0,0 +1,18 @@ +NAME: + enaptercli.test device execute-command - Execute a device command + +USAGE: + enaptercli.test device execute-command [command options] + +OPTIONS: + --verbose log extra details about operation (default: false) + --device-id value device ID + --cmd-name value command name + --cmd-args value command args (should be a JSON string) + --ephemeral run command in ephemeral mode (default: false) + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution b/internal/app/enaptercli/testdata/helps/enapter device execution new file mode 100644 index 0000000..ed07025 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter device execution @@ -0,0 +1,13 @@ +NAME: + enaptercli.test device execution - Manage device command executions + +USAGE: + enaptercli.test device execution command [command options] + +COMMANDS: + list List device command executions + inspect Inspect a device command execution + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution inspect b/internal/app/enaptercli/testdata/helps/enapter device execution inspect new file mode 100644 index 0000000..4069676 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter device execution inspect @@ -0,0 +1,17 @@ +NAME: + enaptercli.test device execution inspect - Inspect a device command execution + +USAGE: + enaptercli.test device execution inspect [command options] + +OPTIONS: + --verbose log extra details about operation (default: false) + --device-id value, -d value device ID + --execution-id value execution ID + --expand value [ --expand value ] coma separated list of expanded options + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution list b/internal/app/enaptercli/testdata/helps/enapter device execution list new file mode 100644 index 0000000..80bbe21 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter device execution list @@ -0,0 +1,16 @@ +NAME: + enaptercli.test device execution list - List device command executions + +USAGE: + enaptercli.test device execution list [command options] + +OPTIONS: + --verbose log extra details about operation (default: false) + --device-id value, -d value device ID + --expand value [ --expand value ] coma separated list of expanded options + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + From 57c682588f719fef7a0b69770b20a0f16a4691cc Mon Sep 17 00:00:00 2001 From: Dmitrii Savchenkov Date: Fri, 14 Mar 2025 17:20:37 +0100 Subject: [PATCH 30/88] fix(rule-engine): validate runtime-version --- internal/app/enaptercli/cmd_base.go | 16 +++++++++++----- internal/app/enaptercli/cmd_device.go | 2 -- internal/app/enaptercli/cmd_rule_engine_rule.go | 9 +++++++-- .../enaptercli/cmd_rule_engine_rule_create.go | 9 ++++++--- .../cmd_rule_engine_rule_update_script.go | 9 ++++++--- .../helps/enapter rule-engine rule create | 2 +- .../helps/enapter rule-engine rule update-script | 2 +- 7 files changed, 32 insertions(+), 17 deletions(-) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index dcafb16..2c4493d 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -218,13 +218,19 @@ func parseRespErrorMessage(resp *http.Response) string { } func validateExpandFlag(cliCtx *cli.Context, supportedFields []string) error { - slices.Sort(supportedFields) - for _, field := range cliCtx.StringSlice("expand") { - if _, ok := slices.BinarySearch(supportedFields, field); !ok { - return fmt.Errorf("%w: %s is not supported by expand, should be one of %s", - errUnsupportedFlagValue, field, supportedFields) + if err := validateFlag("expand", field, supportedFields); err != nil { + return err } } return nil } + +func validateFlag(context, value string, allowedValues []string) error { + slices.Sort(allowedValues) + if _, ok := slices.BinarySearch(allowedValues, value); !ok { + return fmt.Errorf("%w: %s is not supported for %s, should be one of %s", + errUnsupportedFlagValue, value, context, allowedValues) + } + return nil +} diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 17466a9..659e142 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -6,7 +6,6 @@ import ( "fmt" "io" "net/url" - "slices" "github.com/urfave/cli/v2" ) @@ -61,6 +60,5 @@ func (c *cmdDevices) parseAndDumpDeviceLogs(body io.Reader) (int, error) { func (c *cmdDevices) validateExpandFlag(cliCtx *cli.Context) error { supportedFields := []string{"connectivity", "manifest", "properties", "communication_info", "site"} - slices.Sort(supportedFields) return validateExpandFlag(cliCtx, supportedFields) } diff --git a/internal/app/enaptercli/cmd_rule_engine_rule.go b/internal/app/enaptercli/cmd_rule_engine_rule.go index 6b339ba..916789b 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule.go @@ -9,8 +9,8 @@ import ( ) const ( - ruleRuntimeVersion1 = 1 - ruleRuntimeVersion3 = 3 + ruleRuntimeV1 = "V1" + ruleRuntimeV3 = "V3" ) type cmdRuleEngineRule struct { @@ -43,3 +43,8 @@ func (c *cmdRuleEngineRule) doHTTPRequest(ctx context.Context, p doHTTPRequestPa p.Path = path return c.cmdRuleEngine.doHTTPRequest(ctx, p) } + +func (c *cmdRuleEngineRule) validateRuntimeVersion(value string) error { + supportedVersions := []string{ruleRuntimeV1, ruleRuntimeV3} + return validateFlag("runtime-version", value, supportedVersions) +} diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_create.go b/internal/app/enaptercli/cmd_rule_engine_rule_create.go index df566b4..258b757 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_create.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_create.go @@ -17,7 +17,7 @@ type cmdRuleEngineRuleCreate struct { cmdRuleEngineRule slug string scriptPath string - runtimeVersion int + runtimeVersion string execInterval time.Duration disable bool } @@ -50,11 +50,14 @@ func (c *cmdRuleEngineRuleCreate) Flags() []cli.Flag { Destination: &c.scriptPath, Required: true, }, - &cli.IntFlag{ + &cli.StringFlag{ Name: "runtime-version", Usage: "Version of a runtime to use for the script execution", Destination: &c.runtimeVersion, - Value: ruleRuntimeVersion3, + Value: ruleRuntimeV3, + Action: func(_ *cli.Context, v string) error { + return c.validateRuntimeVersion(v) + }, }, &cli.DurationFlag{ Name: "exec-interval", diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go index 513c116..fa46f98 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go @@ -17,7 +17,7 @@ type cmdRuleEngineRuleUpdateScript struct { cmdRuleEngineRule ruleID string scriptPath string - runtimeVersion int + runtimeVersion string execInterval time.Duration } @@ -49,11 +49,14 @@ func (c *cmdRuleEngineRuleUpdateScript) Flags() []cli.Flag { Destination: &c.scriptPath, Required: true, }, - &cli.IntFlag{ + &cli.StringFlag{ Name: "runtime-version", Usage: "Version of a runtime to use for the script execution", Destination: &c.runtimeVersion, - Value: ruleRuntimeVersion3, + Value: ruleRuntimeV3, + Action: func(_ *cli.Context, v string) error { + return c.validateRuntimeVersion(v) + }, }, &cli.DurationFlag{ Name: "exec-interval", diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create index 380415b..7f56691 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create @@ -8,7 +8,7 @@ OPTIONS: --verbose log extra details about operation (default: false) --slug value Slug of a new rule --script value Path to a file containing the script code - --runtime-version value Version of a runtime to use for the script execution (default: 3) + --runtime-version value Version of a runtime to use for the script execution (default: "V3") --exec-interval value How often to execute the script. This option is only compatible with the runtime version 1 (default: 0s) --disable Whether to disable a rule upon creation (default: false) --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script index 315c7e7..6b421da 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script @@ -8,7 +8,7 @@ OPTIONS: --verbose log extra details about operation (default: false) --rule-id value Rule ID or slug to update --script value Path to a file containing the script code - --runtime-version value Version of a runtime to use for the script execution (default: 3) + --runtime-version value Version of a runtime to use for the script execution (default: "V3") --exec-interval value How often to execute the script. This option is only compatible with the runtime version 1 (default: 0s) --help, -h show help From f0769509eaa4774a72a94e8620e2f6636ba369d9 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Thu, 10 Apr 2025 17:15:47 +0400 Subject: [PATCH 31/88] fix: uppercase logs enums --- internal/app/enaptercli/cmd_device_logs.go | 12 ++++++------ internal/app/enaptercli/cmd_device_logsf.go | 2 +- .../enaptercli/testdata/helps/enapter device logs | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/app/enaptercli/cmd_device_logs.go b/internal/app/enaptercli/cmd_device_logs.go index 54a14ae..8c5276e 100644 --- a/internal/app/enaptercli/cmd_device_logs.go +++ b/internal/app/enaptercli/cmd_device_logs.go @@ -75,21 +75,21 @@ func (c *cmdDevicesLogs) Flags() []cli.Flag { Destination: &c.severity, }, &cli.StringFlag{ Name: "order", - Usage: "order logs by criteria (received_at_asc[default], received_at_desc)", + Usage: "order logs by criteria (RECEIVED_AT_ASC[default], RECEIVED_AT_DESC)", Destination: &c.order, Action: func(_ *cli.Context, v string) error { - if v != "received_at_asc" && v != "received_at_desc" { - return fmt.Errorf("%w: should be one of [received_at_asc, received_at_desc]", errUnsupportedFlagValue) + if v != "RECEIVED_AT_ASC" && v != "RECEIVED_AT_DESC" { + return fmt.Errorf("%w: should be one of [RECEIVED_AT_ASC, RECEIVED_AT_DESC]", errUnsupportedFlagValue) } return nil }, }, &cli.StringFlag{ Name: "show", - Usage: "filter logs by criteria (all[default], persist_only, temporary_only)", + Usage: "filter logs by criteria (ALL[default], PERSIST_ONLY, TEMPORARY_ONLY)", Destination: &c.showFilter, Action: func(_ *cli.Context, v string) error { - if v != "all" && v != "persist_only" && v != "temporary_only" { - return fmt.Errorf("%w: should be one of [all, persist_only, temporary_only]", errUnsupportedFlagValue) + if v != "ALL" && v != "PERSIST_ONLY" && v != "TEMPORARY_ONLY" { + return fmt.Errorf("%w: should be one of [ALL, PERSIST_ONLY, TEMPORARY_ONLY]", errUnsupportedFlagValue) } return nil }, diff --git a/internal/app/enaptercli/cmd_device_logsf.go b/internal/app/enaptercli/cmd_device_logsf.go index 9b9ae63..5d342f0 100644 --- a/internal/app/enaptercli/cmd_device_logsf.go +++ b/internal/app/enaptercli/cmd_device_logsf.go @@ -48,7 +48,7 @@ func (c *cmdDevicesLogsf) do(ctx context.Context) error { query := url.Values{} query.Add("received_at_from", time.Now().Add(-time.Hour).UTC().Format(time.RFC3339)) - query.Add("order", "received_at_asc") + query.Add("order", "RECEIVED_AT_ASC") query.Add("limit", strconv.Itoa(singleRequestLimit)) offset := 0 diff --git a/internal/app/enaptercli/testdata/helps/enapter device logs b/internal/app/enaptercli/testdata/helps/enapter device logs index 763674f..8ab90e2 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device logs +++ b/internal/app/enaptercli/testdata/helps/enapter device logs @@ -12,8 +12,8 @@ OPTIONS: --limit value, -l value maximum number of logs to retrieve (default: 0) --offset value, -o value number of logs to skip on retrieve (default: 0) --severity value, -s value filter logs by severity - --order value order logs by criteria (received_at_asc[default], received_at_desc) - --show value filter logs by criteria (all[default], persist_only, temporary_only) + --order value order logs by criteria (RECEIVED_AT_ASC[default], RECEIVED_AT_DESC) + --show value filter logs by criteria (ALL[default], PERSIST_ONLY, TEMPORARY_ONLY) --help, -h show help ENVIRONMENT VARIABLES: From f6ebef4a32f5ae8a2b87e570550a63afb956f80f Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Wed, 7 May 2025 13:45:56 +0400 Subject: [PATCH 32/88] feat: stream device logs via WebSocket --- internal/app/enaptercli/cmd_base.go | 85 ++++++++++++++++--- internal/app/enaptercli/cmd_device.go | 21 ----- internal/app/enaptercli/cmd_device_logs.go | 85 +++++++++++++++++-- internal/app/enaptercli/cmd_device_logsf.go | 83 ------------------ .../enaptercli/cmd_rule_engine_rule_logs.go | 73 ++++------------ .../enaptercli/testdata/helps/enapter device | 1 - .../testdata/helps/enapter device logs | 7 +- .../testdata/helps/enapter device logsf | 15 ---- 8 files changed, 172 insertions(+), 198 deletions(-) delete mode 100644 internal/app/enaptercli/cmd_device_logsf.go delete mode 100644 internal/app/enaptercli/testdata/helps/enapter device logsf diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 2c4493d..3a60a3b 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -116,11 +116,73 @@ func (c *cmdBase) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) erro return p.RespProcessor(resp) } -func (c *cmdBase) dialWebSocket(ctx context.Context, path string) (*websocket.Conn, error) { +type runWebSocketParams struct { + Path string + Query url.Values + RespProcessor func(io.Reader) error +} + +func (c *cmdBase) runWebSocket(ctx context.Context, p runWebSocketParams) error { + for retry := false; ; retry = true { + if retry { + fmt.Fprintln(c.writer, "Reconnecting...") + time.Sleep(time.Second) + } + + conn, err := c.dialWebSocket(ctx, p.Path, p.Query) + if err != nil { + select { + case <-ctx.Done(): + return nil + default: + fmt.Fprintln(c.writer, "Failed to retrieve data:", err) + continue + } + } + fmt.Fprintln(c.writer, "Connection established") + + closeCh := make(chan struct{}) + go func() { + select { + case <-ctx.Done(): + case <-closeCh: + } + conn.Close() + }() + + if err := c.readWebSocket(conn, p.RespProcessor); err != nil { + select { + case <-ctx.Done(): + return nil + default: + fmt.Fprintln(c.writer, "Failed to retrieve data:", err) + close(closeCh) + } + } + } +} + +func (c *cmdBase) defaultRespProcessor(resp *http.Response) error { + if resp.StatusCode != http.StatusOK { + return cli.Exit(parseRespErrorMessage(resp), 1) + } + + n, _ := io.Copy(c.writer, resp.Body) + if n == 0 { + _, _ = io.WriteString(c.writer, "Request finished without body\n") + } + + return nil +} + +func (c *cmdBase) dialWebSocket( + ctx context.Context, path string, query url.Values, +) (*websocket.Conn, error) { url, err := url.Parse(c.apiHost + "/v3" + path) if err != nil { return nil, fmt.Errorf("parse url: %w", err) } + url.RawQuery = query.Encode() switch url.Scheme { case "https": @@ -153,17 +215,18 @@ func (c *cmdBase) dialWebSocket(ctx context.Context, path string) (*websocket.Co return conn, nil } -func (c *cmdBase) defaultRespProcessor(resp *http.Response) error { - if resp.StatusCode != http.StatusOK { - return cli.Exit(parseRespErrorMessage(resp), 1) - } - - n, _ := io.Copy(c.writer, resp.Body) - if n == 0 { - _, _ = io.WriteString(c.writer, "Request finished without body\n") +func (c *cmdBase) readWebSocket( + conn *websocket.Conn, processor func(io.Reader) error, +) error { + for { + _, r, err := conn.NextReader() + if err != nil { + return fmt.Errorf("read: %w", err) + } + if err := processor(r); err != nil { + return err + } } - - return nil } func getRequestBodyString(req *http.Request, contentType string) (string, error) { diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 659e142..7fd4605 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -2,9 +2,7 @@ package enaptercli import ( "context" - "encoding/json" "fmt" - "io" "net/url" "github.com/urfave/cli/v2" @@ -23,7 +21,6 @@ func buildCmdDevices() *cli.Command { buildCmdDevicesInspect(), buildCmdDevicesAssignBlueprint(), buildCmdDevicesLogs(), - buildCmdDevicesLogsf(), buildCmdDevicesDelete(), buildCmdDevicesExecuteCommand(), buildCmdDeviceExecution(), @@ -40,24 +37,6 @@ func (c *cmdDevices) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) e return c.cmdBase.doHTTPRequest(ctx, p) } -func (c *cmdDevices) parseAndDumpDeviceLogs(body io.Reader) (int, error) { - var resp struct { - Logs []struct { - ReceivedAt string `json:"received_at"` - Timestamp string `json:"timestamp"` - Severity string `json:"severity"` - Message string `json:"message"` - } `json:"logs"` - } - if err := json.NewDecoder(body).Decode(&resp); err != nil { - return 0, fmt.Errorf("parse response body: %w", err) - } - for _, l := range resp.Logs { - fmt.Fprintf(c.writer, "%s [%s] %s\n", l.ReceivedAt, l.Severity, l.Message) - } - return len(resp.Logs), nil -} - func (c *cmdDevices) validateExpandFlag(cliCtx *cli.Context) error { supportedFields := []string{"connectivity", "manifest", "properties", "communication_info", "site"} return validateExpandFlag(cliCtx, supportedFields) diff --git a/internal/app/enaptercli/cmd_device_logs.go b/internal/app/enaptercli/cmd_device_logs.go index 8c5276e..b24b63a 100644 --- a/internal/app/enaptercli/cmd_device_logs.go +++ b/internal/app/enaptercli/cmd_device_logs.go @@ -2,6 +2,7 @@ package enaptercli import ( "context" + "encoding/json" "fmt" "io" "net/http" @@ -15,6 +16,7 @@ import ( type cmdDevicesLogs struct { cmdDevices deviceID string + follow bool from cli.Timestamp to cli.Timestamp offset int @@ -46,15 +48,18 @@ func (c *cmdDevicesLogs) Flags() []cli.Flag { Usage: "device ID", Destination: &c.deviceID, Required: true, + }, &cli.BoolFlag{ + Name: "follow", + Aliases: []string{"f"}, + Usage: "follow log output", + Destination: &c.follow, }, &cli.TimestampFlag{ Name: "from", - Aliases: []string{"f"}, Usage: "from timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z)", Destination: &c.from, Layout: time.RFC3339, }, &cli.TimestampFlag{ Name: "to", - Aliases: []string{"t"}, Usage: "to timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z)", Destination: &c.to, Layout: time.RFC3339, @@ -85,11 +90,11 @@ func (c *cmdDevicesLogs) Flags() []cli.Flag { }, }, &cli.StringFlag{ Name: "show", - Usage: "filter logs by criteria (ALL[default], PERSIST_ONLY, TEMPORARY_ONLY)", + Usage: "filter logs by criteria (ALL[default], PERSISTED_ONLY, TEMPORARY_ONLY)", Destination: &c.showFilter, Action: func(_ *cli.Context, v string) error { - if v != "ALL" && v != "PERSIST_ONLY" && v != "TEMPORARY_ONLY" { - return fmt.Errorf("%w: should be one of [ALL, PERSIST_ONLY, TEMPORARY_ONLY]", errUnsupportedFlagValue) + if v != "ALL" && v != "PERSISTED_ONLY" && v != "TEMPORARY_ONLY" { + return fmt.Errorf("%w: should be one of [ALL, PERSISTED_ONLY, TEMPORARY_ONLY]", errUnsupportedFlagValue) } return nil }, @@ -97,6 +102,59 @@ func (c *cmdDevicesLogs) Flags() []cli.Flag { } func (c *cmdDevicesLogs) do(ctx context.Context) error { + if c.follow { + return c.doFollow(ctx) + } + return c.doList(ctx) +} + +func (c *cmdDevicesLogs) doFollow(ctx context.Context) error { + if c.from.Value() != nil { + return cli.Exit("Option received_at_from is unsupported in follow mode.", 1) + } + if c.to.Value() != nil { + return cli.Exit("Option received_at_to is unsupported in follow mode.", 1) + } + if c.offset > 0 { + return cli.Exit("Option offset is unsupported in follow mode.", 1) + } + if c.limit > 0 { + return cli.Exit("Option limit is unsupported in follow mode.", 1) + } + if c.order != "" { + return cli.Exit("Option order is unsupported in follow mode.", 1) + } + + query := url.Values{} + if c.severity != "" { + query.Add("severity", c.severity) + } + if c.showFilter != "" { + query.Add("show", c.showFilter) + } + + path := fmt.Sprintf("/devices/%s/logs", c.deviceID) + + return c.runWebSocket(ctx, runWebSocketParams{ + Path: path, + Query: query, + RespProcessor: func(r io.Reader) error { + var msg struct { + ReceivedAt string `json:"received_at"` + Timestamp string `json:"timestamp"` + Severity string `json:"severity"` + Message string `json:"message"` + } + if err := json.NewDecoder(r).Decode(&msg); err != nil { + return fmt.Errorf("parse payload: %w", err) + } + fmt.Fprintf(c.writer, "%s [%s] %s\n", msg.ReceivedAt, msg.Severity, msg.Message) + return nil + }, + }) +} + +func (c *cmdDevicesLogs) doList(ctx context.Context) error { query := url.Values{} if c.from.Value() != nil { query.Add("received_at_from", c.from.Value().Format(time.RFC3339)) @@ -126,8 +184,21 @@ func (c *cmdDevicesLogs) do(ctx context.Context) error { Query: query, //nolint:bodyclose //body is closed in doHTTPRequest RespProcessor: okRespBodyProcessor(func(body io.Reader) error { - _, err := c.parseAndDumpDeviceLogs(body) - return err + var resp struct { + Logs []struct { + ReceivedAt string `json:"received_at"` + Timestamp string `json:"timestamp"` + Severity string `json:"severity"` + Message string `json:"message"` + } `json:"logs"` + } + if err := json.NewDecoder(body).Decode(&resp); err != nil { + return fmt.Errorf("parse response body: %w", err) + } + for _, l := range resp.Logs { + fmt.Fprintf(c.writer, "%s [%s] %s\n", l.ReceivedAt, l.Severity, l.Message) + } + return nil }), }) } diff --git a/internal/app/enaptercli/cmd_device_logsf.go b/internal/app/enaptercli/cmd_device_logsf.go deleted file mode 100644 index 5d342f0..0000000 --- a/internal/app/enaptercli/cmd_device_logsf.go +++ /dev/null @@ -1,83 +0,0 @@ -package enaptercli - -import ( - "context" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "strconv" - "time" - - "github.com/urfave/cli/v2" -) - -type cmdDevicesLogsf struct { - cmdDevices - deviceID string -} - -func buildCmdDevicesLogsf() *cli.Command { - cmd := &cmdDevicesLogsf{} - return &cli.Command{ - Name: "logsf", - Usage: "Follow device logs", - CustomHelpTemplate: cmd.HelpTemplate(), - Flags: cmd.Flags(), - Before: cmd.Before, - Action: func(cliCtx *cli.Context) error { - return cmd.do(cliCtx.Context) - }, - } -} - -func (c *cmdDevicesLogsf) Flags() []cli.Flag { - flags := c.cmdBase.Flags() - return append(flags, &cli.StringFlag{ - Name: "device-id", - Aliases: []string{"d"}, - Usage: "device ID", - Destination: &c.deviceID, - Required: true, - }) -} - -func (c *cmdDevicesLogsf) do(ctx context.Context) error { - const singleRequestLimit = 10 - - query := url.Values{} - query.Add("received_at_from", time.Now().Add(-time.Hour).UTC().Format(time.RFC3339)) - query.Add("order", "RECEIVED_AT_ASC") - query.Add("limit", strconv.Itoa(singleRequestLimit)) - - offset := 0 - - for { - retryNow := false - err := c.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodGet, - Path: "/" + c.deviceID + "/logs", - Query: query, - //nolint:bodyclose //body is closed in doHTTPRequest - RespProcessor: okRespBodyProcessor(func(body io.Reader) error { - n, err := c.parseAndDumpDeviceLogs(body) - retryNow = n == singleRequestLimit - offset += n - query.Set("offset", strconv.Itoa(offset)) - return err - }), - }) - if err != nil { - if errors.Is(err, context.Canceled) { - return nil - } - fmt.Fprintf(c.writer, "Failed to retrieve logs: %s\n", err) - continue - } - - if !retryNow { - time.Sleep(time.Second) - } - } -} diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_logs.go b/internal/app/enaptercli/cmd_rule_engine_rule_logs.go index 0262eb6..2add98b 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_logs.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_logs.go @@ -3,9 +3,9 @@ package enaptercli import ( "encoding/json" "fmt" + "io" "time" - "github.com/gorilla/websocket" "github.com/urfave/cli/v2" ) @@ -51,63 +51,22 @@ func (c *cmdRuleEngineRuleLogs) do(cliCtx *cli.Context) error { return cli.Exit("Currently, only follow mode (--follow) is supported.", 1) } - path := fmt.Sprintf("/site/rule_engine/rules/%s/logs/ws", c.ruleID) - for retry := false; ; retry = true { - if retry { - fmt.Fprintln(c.writer, "Reconnecting...") - time.Sleep(time.Second) - } + path := fmt.Sprintf("/site/rule_engine/rules/%s/logs", c.ruleID) - conn, err := c.dialWebSocket(cliCtx.Context, path) - if err != nil { - select { - case <-cliCtx.Done(): - return nil - default: - fmt.Fprintln(c.writer, "Failed to retrieve logs:", err) - continue + return c.runWebSocket(cliCtx.Context, runWebSocketParams{ + Path: path, + RespProcessor: func(r io.Reader) error { + var msg struct { + Timestamp int64 `json:"timestamp"` + Severity string `json:"severity"` + Message string `json:"message"` } - } - fmt.Fprintln(c.writer, "Connection established") - - closeCh := make(chan struct{}) - go func() { - select { - case <-cliCtx.Done(): - case <-closeCh: - } - conn.Close() - }() - - if err := c.runProxy(conn); err != nil { - select { - case <-cliCtx.Done(): - return nil - default: - fmt.Fprintln(c.writer, "Failed to retrieve logs:", err) - close(closeCh) + if err := json.NewDecoder(r).Decode(&msg); err != nil { + return fmt.Errorf("parse payload: %w", err) } - } - } -} - -func (c *cmdRuleEngineRuleLogs) runProxy(conn *websocket.Conn) error { - for { - _, r, err := conn.NextReader() - if err != nil { - return fmt.Errorf("read: %w", err) - } - - var msg struct { - Timestamp int64 `json:"timestamp"` - Severity string `json:"severity"` - Message string `json:"message"` - } - if err := json.NewDecoder(r).Decode(&msg); err != nil { - return fmt.Errorf("parse payload: %w", err) - } - - ts := time.Unix(msg.Timestamp, 0).Format(time.RFC3339) - fmt.Fprintf(c.writer, "%s [%s] %s\n", ts, msg.Severity, msg.Message) - } + ts := time.Unix(msg.Timestamp, 0).Format(time.RFC3339) + fmt.Fprintf(c.writer, "%s [%s] %s\n", ts, msg.Severity, msg.Message) + return nil + }, + }) } diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index df05735..be1def9 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -9,7 +9,6 @@ COMMANDS: inspect Inspect a devices assign-blueprint Assign blueprint to device logs Show device logs - logsf Follow device logs delete Delete a device execute-command Execute a device command execution Manage device command executions diff --git a/internal/app/enaptercli/testdata/helps/enapter device logs b/internal/app/enaptercli/testdata/helps/enapter device logs index 8ab90e2..09ac0f5 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device logs +++ b/internal/app/enaptercli/testdata/helps/enapter device logs @@ -7,13 +7,14 @@ USAGE: OPTIONS: --verbose log extra details about operation (default: false) --device-id value, -d value device ID - --from value, -f value from timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z) - --to value, -t value to timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z) + --follow, -f follow log output (default: false) + --from value from timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z) + --to value to timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z) --limit value, -l value maximum number of logs to retrieve (default: 0) --offset value, -o value number of logs to skip on retrieve (default: 0) --severity value, -s value filter logs by severity --order value order logs by criteria (RECEIVED_AT_ASC[default], RECEIVED_AT_DESC) - --show value filter logs by criteria (ALL[default], PERSIST_ONLY, TEMPORARY_ONLY) + --show value filter logs by criteria (ALL[default], PERSISTED_ONLY, TEMPORARY_ONLY) --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter device logsf b/internal/app/enaptercli/testdata/helps/enapter device logsf deleted file mode 100644 index 2b77a87..0000000 --- a/internal/app/enaptercli/testdata/helps/enapter device logsf +++ /dev/null @@ -1,15 +0,0 @@ -NAME: - enaptercli.test device logsf - Follow device logs - -USAGE: - enaptercli.test device logsf [command options] - -OPTIONS: - --verbose log extra details about operation (default: false) - --device-id value, -d value device ID - --help, -h show help - -ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) - From d334735e584ba6a1bc661dec986878c760a70761 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 16 May 2025 18:35:42 +0300 Subject: [PATCH 33/88] feat: list devices from specified site --- internal/app/enaptercli/cmd_device_list.go | 14 ++++++++++++++ .../enaptercli/testdata/helps/enapter device list | 1 + 2 files changed, 15 insertions(+) diff --git a/internal/app/enaptercli/cmd_device_list.go b/internal/app/enaptercli/cmd_device_list.go index 57d053a..277872b 100644 --- a/internal/app/enaptercli/cmd_device_list.go +++ b/internal/app/enaptercli/cmd_device_list.go @@ -11,6 +11,7 @@ import ( type cmdDevicesList struct { cmdDevices + siteID string expand []string } @@ -36,6 +37,10 @@ func (c *cmdDevicesList) Flags() []cli.Flag { Usage: "coma separated list of expanded device info", }, Destination: &c.expand, + }, &cli.StringFlag{ + Name: "site-id", + Usage: "list devices from this site", + Destination: &c.siteID, }) } @@ -52,6 +57,15 @@ func (c *cmdDevicesList) do(ctx context.Context) error { query.Set("expand", strings.Join(c.expand, ",")) } + if c.siteID != "" { + query.Set("site_id", c.siteID) + return c.cmdBase.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/sites/" + c.siteID + "/devices", + Query: query, + }) + } + return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, Path: "", diff --git a/internal/app/enaptercli/testdata/helps/enapter device list b/internal/app/enaptercli/testdata/helps/enapter device list index 3758ce0..be0255c 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device list +++ b/internal/app/enaptercli/testdata/helps/enapter device list @@ -7,6 +7,7 @@ USAGE: OPTIONS: --verbose log extra details about operation (default: false) --expand value [ --expand value ] coma separated list of expanded device info + --site-id value list devices from this site --help, -h show help ENVIRONMENT VARIABLES: From 2b2236d2eec1ccd1fa7f05471833e69c0d521b9d Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 16 May 2025 18:40:10 +0300 Subject: [PATCH 34/88] feat: list and inspect sites --- internal/app/enaptercli/cmd_site.go | 33 +++++++++++++ internal/app/enaptercli/cmd_site_inspect.go | 44 ++++++++++++++++++ internal/app/enaptercli/cmd_site_list.go | 46 +++++++++++++++++++ internal/app/enaptercli/execute.go | 1 + .../app/enaptercli/testdata/helps/enapter | 1 + .../enaptercli/testdata/helps/enapter site | 13 ++++++ .../testdata/helps/enapter site inspect | 15 ++++++ .../testdata/helps/enapter site list | 14 ++++++ 8 files changed, 167 insertions(+) create mode 100644 internal/app/enaptercli/cmd_site.go create mode 100644 internal/app/enaptercli/cmd_site_inspect.go create mode 100644 internal/app/enaptercli/cmd_site_list.go create mode 100644 internal/app/enaptercli/testdata/helps/enapter site create mode 100644 internal/app/enaptercli/testdata/helps/enapter site inspect create mode 100644 internal/app/enaptercli/testdata/helps/enapter site list diff --git a/internal/app/enaptercli/cmd_site.go b/internal/app/enaptercli/cmd_site.go new file mode 100644 index 0000000..b387ad0 --- /dev/null +++ b/internal/app/enaptercli/cmd_site.go @@ -0,0 +1,33 @@ +package enaptercli + +import ( + "context" + "fmt" + "net/url" + + "github.com/urfave/cli/v2" +) + +type cmdSite struct { + cmdBase +} + +func buildCmdSites() *cli.Command { + return &cli.Command{ + Name: "site", + Usage: "Manage sites", + Subcommands: []*cli.Command{ + buildCmdSitesList(), + buildCmdSiteInspect(), + }, + } +} + +func (c *cmdSite) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { + path, err := url.JoinPath("/sites", p.Path) + if err != nil { + return fmt.Errorf("join path: %w", err) + } + p.Path = path + return c.cmdBase.doHTTPRequest(ctx, p) +} diff --git a/internal/app/enaptercli/cmd_site_inspect.go b/internal/app/enaptercli/cmd_site_inspect.go new file mode 100644 index 0000000..32db8d9 --- /dev/null +++ b/internal/app/enaptercli/cmd_site_inspect.go @@ -0,0 +1,44 @@ +package enaptercli + +import ( + "context" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdSiteInspect struct { + cmdSite + siteID string +} + +func buildCmdSiteInspect() *cli.Command { + cmd := &cmdSiteInspect{} + return &cli.Command{ + Name: "inspect", + Usage: "Inspect a site", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdSiteInspect) Flags() []cli.Flag { + flags := c.cmdSite.Flags() + return append(flags, &cli.StringFlag{ + Name: "site-id", + Usage: "site ID", + Destination: &c.siteID, + Required: true, + }) +} + +func (c *cmdSiteInspect) do(ctx context.Context) error { + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/" + c.siteID, + }) +} diff --git a/internal/app/enaptercli/cmd_site_list.go b/internal/app/enaptercli/cmd_site_list.go new file mode 100644 index 0000000..9400566 --- /dev/null +++ b/internal/app/enaptercli/cmd_site_list.go @@ -0,0 +1,46 @@ +package enaptercli + +import ( + "context" + "net/http" + "net/url" + "strconv" + + "github.com/urfave/cli/v2" +) + +type cmdSitesList struct { + cmdSite + offset int + limit int +} + +func buildCmdSitesList() *cli.Command { + cmd := &cmdSitesList{} + return &cli.Command{ + Name: "list", + Usage: "List user sites", + CustomHelpTemplate: cmd.HelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdSitesList) do(ctx context.Context) error { + query := url.Values{} + if c.offset != 0 { + query.Set("offset", strconv.Itoa(c.offset)) + } + if c.limit != 0 { + query.Set("limit", strconv.Itoa(c.limit)) + } + + return c.cmdBase.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/users/me/sites", + Query: query, + }) +} diff --git a/internal/app/enaptercli/execute.go b/internal/app/enaptercli/execute.go index 761ba1f..9785c34 100644 --- a/internal/app/enaptercli/execute.go +++ b/internal/app/enaptercli/execute.go @@ -21,6 +21,7 @@ func NewApp() *cli.App { "The token can be obtained in your Enapter Cloud account settings." app.Commands = []*cli.Command{ + buildCmdSites(), buildCmdDevices(), buildCmdBlueprints(), buildCmdProvisioning(), diff --git a/internal/app/enaptercli/testdata/helps/enapter b/internal/app/enaptercli/testdata/helps/enapter index d3a1e39..05404c9 100644 --- a/internal/app/enaptercli/testdata/helps/enapter +++ b/internal/app/enaptercli/testdata/helps/enapter @@ -8,6 +8,7 @@ DESCRIPTION: Enapter CLI requires access token for authentication. The token can be obtained in your Enapter Cloud account settings. COMMANDS: + site Manage sites device Manage devices blueprint Manage blueprints provisioning Create devices of different types diff --git a/internal/app/enaptercli/testdata/helps/enapter site b/internal/app/enaptercli/testdata/helps/enapter site new file mode 100644 index 0000000..481d79f --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter site @@ -0,0 +1,13 @@ +NAME: + enaptercli.test site - Manage sites + +USAGE: + enaptercli.test site command [command options] + +COMMANDS: + list List user sites + inspect Inspect a site + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter site inspect b/internal/app/enaptercli/testdata/helps/enapter site inspect new file mode 100644 index 0000000..6cc9598 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter site inspect @@ -0,0 +1,15 @@ +NAME: + enaptercli.test site inspect - Inspect a site + +USAGE: + enaptercli.test site inspect [command options] + +OPTIONS: + --verbose log extra details about operation (default: false) + --site-id value site ID + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + diff --git a/internal/app/enaptercli/testdata/helps/enapter site list b/internal/app/enaptercli/testdata/helps/enapter site list new file mode 100644 index 0000000..34b220a --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter site list @@ -0,0 +1,14 @@ +NAME: + enaptercli.test site list - List user sites + +USAGE: + enaptercli.test site list [command options] + +OPTIONS: + --verbose log extra details about operation (default: false) + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + From 7fe0250c87f1e82325f9327746da3821e31c062b Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 20 May 2025 10:35:42 +0300 Subject: [PATCH 35/88] tests: check request --- internal/app/enaptercli/execute_test.go | 4 ++++ .../testdata/http_req_resp/blueprint_inspect_by_id/req_0 | 3 +++ .../testdata/http_req_resp/blueprint_inspect_by_name/req_0 | 3 +++ .../testdata/http_req_resp/device_assign_blueprint/req_0 | 3 +++ 4 files changed, 13 insertions(+) diff --git a/internal/app/enaptercli/execute_test.go b/internal/app/enaptercli/execute_test.go index 1b9995c..546fbc0 100644 --- a/internal/app/enaptercli/execute_test.go +++ b/internal/app/enaptercli/execute_test.go @@ -73,10 +73,14 @@ func TestHTTPReqResp(t *testing.T) { if update { err := os.WriteFile(expReqFileName, shouldMarshalIndent(t, reqObj), 0o600) require.NoError(t, err) + } else { + require.Equal(t, readFileToString(t, expReqFileName), string(shouldMarshalIndent(t, reqObj))) } resp := shouldReadFile(t, filepath.Join(testdataPath, tc.Name(), "resp_"+strconv.Itoa(reqCount))) _, _ = w.Write(resp) + + reqCount++ })) defer srv.Close() diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/req_0 index 8a06bd6..75d3ce7 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/req_0 +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/req_0 @@ -5,6 +5,9 @@ "Accept-Encoding": [ "gzip" ], + "Content-Type": [ + "" + ], "User-Agent": [ "Go-http-client/1.1" ], diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/req_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/req_0 index 5ee90c8..89ac6aa 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/req_0 +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/req_0 @@ -5,6 +5,9 @@ "Accept-Encoding": [ "gzip" ], + "Content-Type": [ + "" + ], "User-Agent": [ "Go-http-client/1.1" ], diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/req_0 index e8eeabb..490ced4 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/req_0 +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/req_0 @@ -8,6 +8,9 @@ "Content-Length": [ "55" ], + "Content-Type": [ + "application/json" + ], "User-Agent": [ "Go-http-client/1.1" ], From fe5bdac2a1548f817277ce88f8eb62dac2d217f4 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 20 May 2025 10:35:19 +0300 Subject: [PATCH 36/88] feat: assign-blueprint and provision lua-device with blueprint from path --- .../app/enaptercli/cmd_blueprints_upload.go | 38 +++++++++++++++++-- .../enaptercli/cmd_device_assign_blueprint.go | 33 ++++++++++++++-- .../enaptercli/cmd_provisioning_lua_device.go | 35 ++++++++++++++--- .../app/enaptercli/testdata/blueprints/bp.zip | 1 + .../testdata/blueprints/simple/firmware.lua | 1 + .../testdata/blueprints/simple/manifest.yml | 7 ++++ .../helps/enapter device assign-blueprint | 1 + .../helps/enapter provisioning lua-device | 1 + .../cmd.tmpl | 0 .../out | 0 .../req_0 | 0 .../resp_0 | 0 .../cmd.tmpl | 1 + .../out | 1 + .../device_assign_blueprint_by_path/cmd.tmpl | 1 + .../device_assign_blueprint_by_path/out | 1 + .../device_assign_blueprint_by_path/req_0 | 22 +++++++++++ .../device_assign_blueprint_by_path/req_1 | 22 +++++++++++ .../device_assign_blueprint_by_path/resp_0 | 1 + .../device_assign_blueprint_by_path/resp_1 | 1 + .../device_assign_blueprint_by_zip/cmd.tmpl | 1 + .../device_assign_blueprint_by_zip/out | 1 + .../device_assign_blueprint_by_zip/req_0 | 22 +++++++++++ .../device_assign_blueprint_by_zip/req_1 | 22 +++++++++++ .../device_assign_blueprint_by_zip/resp_0 | 1 + .../device_assign_blueprint_by_zip/resp_1 | 1 + .../cmd.tmpl | 1 + .../out | 1 + 28 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 internal/app/enaptercli/testdata/blueprints/bp.zip create mode 100644 internal/app/enaptercli/testdata/blueprints/simple/firmware.lua create mode 100644 internal/app/enaptercli/testdata/blueprints/simple/manifest.yml rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint => device_assign_blueprint_by_id}/cmd.tmpl (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint => device_assign_blueprint_by_id}/out (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint => device_assign_blueprint_by_id}/req_0 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint => device_assign_blueprint_by_id}/resp_0 (100%) create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_1 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/resp_1 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_1 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/resp_1 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/out diff --git a/internal/app/enaptercli/cmd_blueprints_upload.go b/internal/app/enaptercli/cmd_blueprints_upload.go index 683160d..4785ce3 100644 --- a/internal/app/enaptercli/cmd_blueprints_upload.go +++ b/internal/app/enaptercli/cmd_blueprints_upload.go @@ -3,6 +3,7 @@ package enaptercli import ( "bytes" "context" + "encoding/json" "fmt" "net/http" "os" @@ -41,25 +42,54 @@ func (c *cmdBlueprintsUpload) Flags() []cli.Flag { } func (c *cmdBlueprintsUpload) upload(ctx context.Context) error { - fi, err := os.Stat(c.blueprintPath) + return uploadBlueprint(ctx, c.blueprintPath, c.doHTTPRequest) +} + +func uploadBlueprintAndReturnBlueprintID(ctx context.Context, blueprintPath string, + doHTTPRequest func(context.Context, doHTTPRequestParams) error, +) (string, error) { + var blueprintID string + err := uploadBlueprint(ctx, blueprintPath, func(ctx context.Context, reqParams doHTTPRequestParams) error { + reqParams.RespProcessor = func(resp *http.Response) error { + var respBlueprint struct { + Blueprint struct { + ID string `json:"id"` + } `json:"blueprint"` + } + if err := json.NewDecoder(resp.Body).Decode(&respBlueprint); err != nil { + return fmt.Errorf("decode blueprint response: %w", err) + } + blueprintID = respBlueprint.Blueprint.ID + return nil + } + return doHTTPRequest(ctx, reqParams) + }) + return blueprintID, err +} + +func uploadBlueprint( + ctx context.Context, blueprintPath string, + doHTTPRequest func(context.Context, doHTTPRequestParams) error, +) error { + fi, err := os.Stat(blueprintPath) if err != nil { return fmt.Errorf("check blueprint path: %w", err) } var data []byte if fi.IsDir() { - data, err = zipDir(c.blueprintPath) + data, err = zipDir(blueprintPath) if err != nil { return fmt.Errorf("zip blueprint directory: %w", err) } } else { - data, err = os.ReadFile(c.blueprintPath) + data, err = os.ReadFile(blueprintPath) if err != nil { return fmt.Errorf("read blueprint zip file: %w", err) } } - return c.doHTTPRequest(ctx, doHTTPRequestParams{ + return doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodPost, Path: "/blueprints/upload", Body: bytes.NewReader(data), diff --git a/internal/app/enaptercli/cmd_device_assign_blueprint.go b/internal/app/enaptercli/cmd_device_assign_blueprint.go index 752087d..ee5f8b2 100644 --- a/internal/app/enaptercli/cmd_device_assign_blueprint.go +++ b/internal/app/enaptercli/cmd_device_assign_blueprint.go @@ -12,8 +12,9 @@ import ( type cmdDevicesAssignBlueprint struct { cmdDevices - deviceID string - blueprintID string + deviceID string + blueprintID string + blueprintPath string } func buildCmdDevicesAssignBlueprint() *cli.Command { @@ -43,12 +44,36 @@ func (c *cmdDevicesAssignBlueprint) Flags() []cli.Flag { Aliases: []string{"b"}, Usage: "blueprint ID to assign", Destination: &c.blueprintID, - Required: true, + }, &cli.StringFlag{ + Name: "blueprint-path", + Usage: "blueprint path (zip file or directory) to assign", + Destination: &c.blueprintPath, }) } +func (c *cmdDevicesAssignBlueprint) Before(cliCtx *cli.Context) error { + if err := c.cmdDevices.Before(cliCtx); err != nil { + return err + } + if c.blueprintID != "" && c.blueprintPath != "" { + return fmt.Errorf("only one of --blueprint-id or --blueprint-path can be specified") + } + if c.blueprintID == "" && c.blueprintPath == "" { + return fmt.Errorf("one of --blueprint-id or --blueprint-path must be specified") + } + return c.validateExpandFlag(cliCtx) +} + func (c *cmdDevicesAssignBlueprint) do(ctx context.Context) error { - body, err := json.Marshal(map[string]interface{}{ + if c.blueprintPath != "" { + blueprintID, err := uploadBlueprintAndReturnBlueprintID(ctx, c.blueprintPath, c.cmdBase.doHTTPRequest) + if err != nil { + return fmt.Errorf("upload blueprint: %w", err) + } + c.blueprintID = blueprintID + } + + body, err := json.Marshal(map[string]any{ "blueprint_id": c.blueprintID, }) if err != nil { diff --git a/internal/app/enaptercli/cmd_provisioning_lua_device.go b/internal/app/enaptercli/cmd_provisioning_lua_device.go index 2935041..5050ca1 100644 --- a/internal/app/enaptercli/cmd_provisioning_lua_device.go +++ b/internal/app/enaptercli/cmd_provisioning_lua_device.go @@ -12,10 +12,11 @@ import ( type cmdProvisioningLua struct { cmdProvisioning - deviceName string - deviceSlug string - runtimeID string - blueprintID string + deviceName string + deviceSlug string + runtimeID string + blueprintID string + blueprintPath string } func buildCmdProvisioningLua() *cli.Command { @@ -55,11 +56,35 @@ func (c *cmdProvisioningLua) Flags() []cli.Flag { Aliases: []string{"b"}, Usage: "blueprint ID of a new Lua device", Destination: &c.blueprintID, - Required: true, + }, &cli.StringFlag{ + Name: "blueprint-path", + Usage: "blueprint path (zip file or directory) to assign", + Destination: &c.blueprintPath, }) } +func (c *cmdProvisioningLua) Before(cliCtx *cli.Context) error { + if err := c.cmdProvisioning.Before(cliCtx); err != nil { + return err + } + if c.blueprintID != "" && c.blueprintPath != "" { + return fmt.Errorf("only one of --blueprint-id or --blueprint-path can be specified") + } + if c.blueprintID == "" && c.blueprintPath == "" { + return fmt.Errorf("one of --blueprint-id or --blueprint-path must be specified") + } + return nil +} + func (c *cmdProvisioningLua) do(ctx context.Context) error { + if c.blueprintPath != "" { + blueprintID, err := uploadBlueprintAndReturnBlueprintID(ctx, c.blueprintPath, c.cmdBase.doHTTPRequest) + if err != nil { + return fmt.Errorf("upload blueprint: %w", err) + } + c.blueprintID = blueprintID + } + body, err := json.Marshal(map[string]interface{}{ "runtime_id": c.runtimeID, "name": c.deviceName, diff --git a/internal/app/enaptercli/testdata/blueprints/bp.zip b/internal/app/enaptercli/testdata/blueprints/bp.zip new file mode 100644 index 0000000..5150cb7 --- /dev/null +++ b/internal/app/enaptercli/testdata/blueprints/bp.zip @@ -0,0 +1 @@ +blueprint.zip diff --git a/internal/app/enaptercli/testdata/blueprints/simple/firmware.lua b/internal/app/enaptercli/testdata/blueprints/simple/firmware.lua new file mode 100644 index 0000000..aa6fd65 --- /dev/null +++ b/internal/app/enaptercli/testdata/blueprints/simple/firmware.lua @@ -0,0 +1 @@ +enapter.log("Hello from firmware.lua") diff --git a/internal/app/enaptercli/testdata/blueprints/simple/manifest.yml b/internal/app/enaptercli/testdata/blueprints/simple/manifest.yml new file mode 100644 index 0000000..fc8f08a --- /dev/null +++ b/internal/app/enaptercli/testdata/blueprints/simple/manifest.yml @@ -0,0 +1,7 @@ +blueprint_spec: device/3.0 +display_name: Simple Lua + +runtime: + type: lua + options: + file: firmware.lua diff --git a/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint b/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint index b29b84e..675510e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint @@ -8,6 +8,7 @@ OPTIONS: --verbose log extra details about operation (default: false) --device-id value, -d value device ID --blueprint-id value, -b value blueprint ID to assign + --blueprint-path value blueprint path (zip file or directory) to assign --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device b/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device index a0ad5ed..e7d175c 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device @@ -10,6 +10,7 @@ OPTIONS: --device-name value, -n value name of a new Lua device --device-slug value slug of a new Lua device --blueprint-id value, -b value blueprint ID of a new Lua device + --blueprint-path value blueprint path (zip file or directory) to assign --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/cmd.tmpl similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/cmd.tmpl rename to internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/cmd.tmpl diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/out b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/out similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/out rename to internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/out diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/req_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/req_0 rename to internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/req_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/resp_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint/resp_0 rename to internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/resp_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/cmd.tmpl new file mode 100644 index 0000000..ab9cde0 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/cmd.tmpl @@ -0,0 +1 @@ +enapter3 device assign-blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 --blueprint-path ./testdata/blueprints/bp.zip diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/out b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/out new file mode 100644 index 0000000..dc69131 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/out @@ -0,0 +1 @@ +app exit with error: only one of --blueprint-id or --blueprint-path can be specified diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/cmd.tmpl new file mode 100644 index 0000000..33e057f --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/cmd.tmpl @@ -0,0 +1 @@ +enapter3 device assign-blueprint {{.BaseFlags}} --device-id 3b0a0626-2dc4-44a3-ac5a-34d58b7b2a26 --blueprint-path ./testdata/blueprints/simple diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/out b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/out new file mode 100644 index 0000000..f134b3a --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/out @@ -0,0 +1 @@ +{"blueprint": "assigned by path"} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_0 new file mode 100644 index 0000000..8fe0705 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_0 @@ -0,0 +1,22 @@ +{ + "Method": "POST", + "URL": "/v3/blueprints/upload", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Length": [ + "400" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "PK\u0003\u0004\u0014\u0000\b\u0000\b\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\f\u0000\u0000\u0000firmware.luaJ\ufffdK,(I-\ufffd\ufffd\ufffdO\ufffdP\ufffdH\ufffd\ufffd\ufffdWH+\ufffd\ufffdUH\ufffd,\ufffd-O,J\ufffd\ufffd)MT\ufffd\ufffd\u0002\u0004\u0000\u0000\ufffd\ufffdPK\u0007\b\ufffd\ufffdv*-\u0000\u0000\u0000'\u0000\u0000\u0000PK\u0003\u0004\u0014\u0000\b\u0000\b\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\f\u0000\u0000\u0000manifest.yml\u0014\ufffd˩\ufffd@\f\u0005н\ufffd\ufffd\u0015\ufffd=\ufffdN5d\ufffd\u0002\ufffdb_\ufffd@3\u0016\ufffdIp\ufffd\ufffd\ufffd\ufffdy\ufffdd6\ufffdc\ufffd\ufffdM\ufffd\ufffd\ufffd\u001b\ufffd\u001e˿\ufffd\ufffd3\ufffdZ\ufffd\u0015*^^2\ufffd\ufffd4\ufffd6\ufffd\ufffdB\u0015`\\IEL\u0013\ufffd\ufffd\ufffdg\ufffd7\u0003\ufffd\u0007\u0015\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd\u001e\ufffd\u0000\u0000\u0000\ufffd\ufffdPK\u0007\b*\u0011\u0019Ae\u0000\u0000\u0000l\u0000\u0000\u0000PK\u0001\u0002\u0014\u0000\u0014\u0000\b\u0000\b\u0000\u0000\u0000\u0000\u0000\ufffd\ufffdv*-\u0000\u0000\u0000'\u0000\u0000\u0000\f\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000firmware.luaPK\u0001\u0002\u0014\u0000\u0014\u0000\b\u0000\b\u0000\u0000\u0000\u0000\u0000*\u0011\u0019Ae\u0000\u0000\u0000l\u0000\u0000\u0000\f\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000g\u0000\u0000\u0000manifest.ymlPK\u0005\u0006\u0000\u0000\u0000\u0000\u0002\u0000\u0002\u0000t\u0000\u0000\u0000\u0006\u0001\u0000\u0000\u0000\u0000" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_1 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_1 new file mode 100644 index 0000000..b2be2c4 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_1 @@ -0,0 +1,22 @@ +{ + "Method": "POST", + "URL": "/v3/devices/3b0a0626-2dc4-44a3-ac5a-34d58b7b2a26/assign_blueprint", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Length": [ + "35" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "{\"blueprint_id\":\"new_blueprint_id\"}" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/resp_0 new file mode 100644 index 0000000..deb78ad --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/resp_0 @@ -0,0 +1 @@ +{ "blueprint": {"id": "new_blueprint_id"} } diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/resp_1 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/resp_1 new file mode 100644 index 0000000..f134b3a --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/resp_1 @@ -0,0 +1 @@ +{"blueprint": "assigned by path"} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/cmd.tmpl new file mode 100644 index 0000000..ab232f3 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/cmd.tmpl @@ -0,0 +1 @@ +enapter3 device assign-blueprint {{.BaseFlags}} --device-id 3b0a0626-2dc4-44a3-ac5a-34d58b7b2a26 --blueprint-path ./testdata/blueprints/bp.zip diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/out b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/out new file mode 100644 index 0000000..c815626 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/out @@ -0,0 +1 @@ +{"blueprint": "assigned by zip"} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_0 new file mode 100644 index 0000000..b91ee14 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_0 @@ -0,0 +1,22 @@ +{ + "Method": "POST", + "URL": "/v3/blueprints/upload", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Length": [ + "14" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "blueprint.zip\n" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_1 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_1 new file mode 100644 index 0000000..141edbe --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_1 @@ -0,0 +1,22 @@ +{ + "Method": "POST", + "URL": "/v3/devices/3b0a0626-2dc4-44a3-ac5a-34d58b7b2a26/assign_blueprint", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Length": [ + "40" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "Go-http-client/1.1" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "{\"blueprint_id\":\"blueprint_id_from_zip\"}" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/resp_0 new file mode 100644 index 0000000..34acd0a --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/resp_0 @@ -0,0 +1 @@ +{ "blueprint": {"id": "blueprint_id_from_zip"} } diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/resp_1 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/resp_1 new file mode 100644 index 0000000..c815626 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/resp_1 @@ -0,0 +1 @@ +{"blueprint": "assigned by zip"} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/cmd.tmpl new file mode 100644 index 0000000..bace8f1 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/cmd.tmpl @@ -0,0 +1 @@ +enapter3 device assign-blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/out b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/out new file mode 100644 index 0000000..8ccadaf --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/out @@ -0,0 +1 @@ +app exit with error: one of --blueprint-id or --blueprint-path must be specified From a317541d05fa03cc5b34b95010861e37794a7851 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 20 May 2025 10:49:20 +0300 Subject: [PATCH 37/88] chore: bump go version to 1.23 --- .github/workflows/ci.yml | 6 +++--- .github/workflows/tags.yml | 2 +- .golangci.yml | 12 ++++++++---- go.mod | 3 ++- .../app/enaptercli/cmd_device_assign_blueprint.go | 4 ++-- .../app/enaptercli/cmd_provisioning_lua_device.go | 4 ++-- internal/app/enaptercli/errors.go | 2 ++ internal/app/enaptercli/execute_test.go | 6 +----- 8 files changed, 21 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2320ae..11a51b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v5 with: - go-version: ~1.21 + go-version: ~1.23 id: go - name: Check out code into the Go module directory uses: actions/checkout@v4 @@ -28,8 +28,8 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: ~1.21 + go-version: ~1.23 - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: - version: v1.56.2 + version: v1.64.8 diff --git a/.github/workflows/tags.yml b/.github/workflows/tags.yml index 1d56636..23734b3 100644 --- a/.github/workflows/tags.yml +++ b/.github/workflows/tags.yml @@ -13,7 +13,7 @@ jobs: - name: Install Go uses: actions/setup-go@v5 with: - go-version: ~1.21 + go-version: ~1.23 - name: Create release id: goreleaser diff --git a/.golangci.yml b/.golangci.yml index 4cddfe8..60abab6 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,15 +6,17 @@ linters: enable: - asciicheck - bodyclose + - copyloopvar # - deadcode # - depguard - dogsled # - dupl # some commands are looks very similar in implementation + - err113 - errcheck - errorlint - exhaustive # - exhaustivestruct - - exportloopref + # - exportloopref - funlen - gci - gochecknoglobals @@ -25,13 +27,11 @@ linters: - gocyclo - godot # - godox - - goerr113 - gofmt - gofumpt - goheader - goimports # - golint - - gomnd - gomodguard - goprintffuncname - gosec @@ -42,6 +42,7 @@ linters: - lll # - maligned - misspell + - mnd - nakedret - nestif # - nlreturn @@ -70,7 +71,10 @@ linters-settings: lll: line-length: 110 gci: - local-prefixes: github.com/enapter/enapter-cli + sections: + - standard + - default + - prefix(github.com/enapter/enapter-cli/) issues: exclude-rules: diff --git a/go.mod b/go.mod index 519abbb..d8f77cc 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,7 @@ module github.com/enapter/enapter-cli -go 1.21 +go 1.23.0 + require ( github.com/gorilla/websocket v1.5.3 diff --git a/internal/app/enaptercli/cmd_device_assign_blueprint.go b/internal/app/enaptercli/cmd_device_assign_blueprint.go index ee5f8b2..dbd860b 100644 --- a/internal/app/enaptercli/cmd_device_assign_blueprint.go +++ b/internal/app/enaptercli/cmd_device_assign_blueprint.go @@ -56,10 +56,10 @@ func (c *cmdDevicesAssignBlueprint) Before(cliCtx *cli.Context) error { return err } if c.blueprintID != "" && c.blueprintPath != "" { - return fmt.Errorf("only one of --blueprint-id or --blueprint-path can be specified") + return errOnlyOneBlueprinFlag } if c.blueprintID == "" && c.blueprintPath == "" { - return fmt.Errorf("one of --blueprint-id or --blueprint-path must be specified") + return errMissedBlueprintFlag } return c.validateExpandFlag(cliCtx) } diff --git a/internal/app/enaptercli/cmd_provisioning_lua_device.go b/internal/app/enaptercli/cmd_provisioning_lua_device.go index 5050ca1..00b43a2 100644 --- a/internal/app/enaptercli/cmd_provisioning_lua_device.go +++ b/internal/app/enaptercli/cmd_provisioning_lua_device.go @@ -68,10 +68,10 @@ func (c *cmdProvisioningLua) Before(cliCtx *cli.Context) error { return err } if c.blueprintID != "" && c.blueprintPath != "" { - return fmt.Errorf("only one of --blueprint-id or --blueprint-path can be specified") + return errOnlyOneBlueprinFlag } if c.blueprintID == "" && c.blueprintPath == "" { - return fmt.Errorf("one of --blueprint-id or --blueprint-path must be specified") + return errMissedBlueprintFlag } return nil } diff --git a/internal/app/enaptercli/errors.go b/internal/app/enaptercli/errors.go index 7f05d22..b1e45a7 100644 --- a/internal/app/enaptercli/errors.go +++ b/internal/app/enaptercli/errors.go @@ -6,4 +6,6 @@ var ( errAPITokenMissed = errors.New("API token missing. Set it up using environment " + "variable ENAPTER3_API_TOKEN") errUnsupportedFlagValue = errors.New("unsupported flag value") + errOnlyOneBlueprinFlag = errors.New("only one of --blueprint-id or --blueprint-path can be specified") + errMissedBlueprintFlag = errors.New("one of --blueprint-id or --blueprint-path must be specified") ) diff --git a/internal/app/enaptercli/execute_test.go b/internal/app/enaptercli/execute_test.go index 546fbc0..9e2d0fe 100644 --- a/internal/app/enaptercli/execute_test.go +++ b/internal/app/enaptercli/execute_test.go @@ -23,7 +23,6 @@ func TestHelpMessages(t *testing.T) { require.NoError(t, err) for _, fi := range files { - fi := fi t.Run(fi.Name(), func(t *testing.T) { args := strings.Split(fi.Name(), " ") args = append(args, "-h") @@ -54,7 +53,6 @@ func TestHTTPReqResp(t *testing.T) { require.NoError(t, err) for _, tc := range tests { - tc := tc t.Run(tc.Name(), func(t *testing.T) { reqCount := 0 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -84,9 +82,7 @@ func TestHTTPReqResp(t *testing.T) { })) defer srv.Close() - tmplParams := struct { - BaseFlags string - }{ + tmplParams := struct{ BaseFlags string }{ BaseFlags: strings.Join([]string{"--token", testToken, "--api-host", srv.URL}, " "), } From 119cbe3749ba2d567a34cb39e60c17fc7acf27a5 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 27 Jun 2025 13:22:09 +0300 Subject: [PATCH 38/88] feat: rename inspect -> get --- internal/app/enaptercli/cmd_blueprints.go | 2 +- ...prints_inspect.go => cmd_blueprints_get.go} | 18 +++++++++--------- internal/app/enaptercli/cmd_device.go | 2 +- .../app/enaptercli/cmd_device_execution.go | 2 +- ..._inspect.go => cmd_device_execution_get.go} | 16 ++++++++-------- ...cmd_device_inspect.go => cmd_device_get.go} | 16 ++++++++-------- internal/app/enaptercli/cmd_rule_engine.go | 2 +- ...ngine_inspect.go => cmd_rule_engine_get.go} | 12 ++++++------ .../app/enaptercli/cmd_rule_engine_rule.go | 2 +- ..._inspect.go => cmd_rule_engine_rule_get.go} | 14 +++++++------- internal/app/enaptercli/cmd_site.go | 2 +- .../{cmd_site_inspect.go => cmd_site_get.go} | 14 +++++++------- .../testdata/helps/enapter blueprint | 2 +- ...blueprint inspect => enapter blueprint get} | 6 +++--- .../enaptercli/testdata/helps/enapter device | 2 +- .../testdata/helps/enapter device execution | 2 +- ...on inspect => enapter device execution get} | 4 ++-- ...apter device inspect => enapter device get} | 4 ++-- .../testdata/helps/enapter rule-engine | 2 +- ...-engine inspect => enapter rule-engine get} | 4 ++-- .../testdata/helps/enapter rule-engine rule | 2 +- ...le inspect => enapter rule-engine rule get} | 4 ++-- .../app/enaptercli/testdata/helps/enapter site | 2 +- .../{enapter site inspect => enapter site get} | 4 ++-- .../blueprint_inspect_by_id/cmd.tmpl | 2 +- .../blueprint_inspect_by_name/cmd.tmpl | 2 +- 26 files changed, 72 insertions(+), 72 deletions(-) rename internal/app/enaptercli/{cmd_blueprints_inspect.go => cmd_blueprints_get.go} (68%) rename internal/app/enaptercli/{cmd_device_execution_inspect.go => cmd_device_execution_get.go} (73%) rename internal/app/enaptercli/{cmd_device_inspect.go => cmd_device_get.go} (75%) rename internal/app/enaptercli/{cmd_rule_engine_inspect.go => cmd_rule_engine_get.go} (61%) rename internal/app/enaptercli/{cmd_rule_engine_rule_inspect.go => cmd_rule_engine_rule_get.go} (66%) rename internal/app/enaptercli/{cmd_site_inspect.go => cmd_site_get.go} (69%) rename internal/app/enaptercli/testdata/helps/{enapter blueprint inspect => enapter blueprint get} (62%) rename internal/app/enaptercli/testdata/helps/{enapter device execution inspect => enapter device execution get} (77%) rename internal/app/enaptercli/testdata/helps/{enapter device inspect => enapter device get} (80%) rename internal/app/enaptercli/testdata/helps/{enapter rule-engine inspect => enapter rule-engine get} (68%) rename internal/app/enaptercli/testdata/helps/{enapter rule-engine rule inspect => enapter rule-engine rule get} (71%) rename internal/app/enaptercli/testdata/helps/{enapter site inspect => enapter site get} (75%) diff --git a/internal/app/enaptercli/cmd_blueprints.go b/internal/app/enaptercli/cmd_blueprints.go index 9acbd1b..5dd161e 100644 --- a/internal/app/enaptercli/cmd_blueprints.go +++ b/internal/app/enaptercli/cmd_blueprints.go @@ -18,7 +18,7 @@ func buildCmdBlueprints() *cli.Command { buildCmdBlueprintsProfiles(), buildCmdBlueprintsUpload(), buildCmdBlueprintsDownload(), - buildCmdBlueprintsInspect(), + buildCmdBlueprintsGet(), }, } } diff --git a/internal/app/enaptercli/cmd_blueprints_inspect.go b/internal/app/enaptercli/cmd_blueprints_get.go similarity index 68% rename from internal/app/enaptercli/cmd_blueprints_inspect.go rename to internal/app/enaptercli/cmd_blueprints_get.go index 6e8628f..23c0ba4 100644 --- a/internal/app/enaptercli/cmd_blueprints_inspect.go +++ b/internal/app/enaptercli/cmd_blueprints_get.go @@ -7,37 +7,37 @@ import ( "github.com/urfave/cli/v2" ) -type cmdBlueprintsInpsect struct { +type cmdBlueprintsGet struct { cmdBlueprints blueprintID string } -func buildCmdBlueprintsInspect() *cli.Command { - cmd := &cmdBlueprintsInpsect{} +func buildCmdBlueprintsGet() *cli.Command { + cmd := &cmdBlueprintsGet{} return &cli.Command{ - Name: "inspect", - Usage: "Get blueprint metainfo", + Name: "get", + Usage: "Get blueprint metadata", CustomHelpTemplate: cmd.HelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { - return cmd.inspect(cliCtx.Context) + return cmd.get(cliCtx.Context) }, } } -func (c *cmdBlueprintsInpsect) Flags() []cli.Flag { +func (c *cmdBlueprintsGet) Flags() []cli.Flag { flags := c.cmdBlueprints.Flags() return append(flags, &cli.StringFlag{ Name: "blueprint-id", Aliases: []string{"b"}, - Usage: "blueprint name or ID to inspect", + Usage: "blueprint name or ID to get", Destination: &c.blueprintID, Required: true, }) } -func (c *cmdBlueprintsInpsect) inspect(ctx context.Context) error { +func (c *cmdBlueprintsGet) get(ctx context.Context) error { if isBlueprintID(c.blueprintID) { return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 7fd4605..89f9cc8 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -18,7 +18,7 @@ func buildCmdDevices() *cli.Command { Usage: "Manage devices", Subcommands: []*cli.Command{ buildCmdDevicesList(), - buildCmdDevicesInspect(), + buildCmdDevicesGet(), buildCmdDevicesAssignBlueprint(), buildCmdDevicesLogs(), buildCmdDevicesDelete(), diff --git a/internal/app/enaptercli/cmd_device_execution.go b/internal/app/enaptercli/cmd_device_execution.go index f4bd38a..f8d1652 100644 --- a/internal/app/enaptercli/cmd_device_execution.go +++ b/internal/app/enaptercli/cmd_device_execution.go @@ -19,7 +19,7 @@ func buildCmdDeviceExecution() *cli.Command { Usage: "Manage device command executions", Subcommands: []*cli.Command{ buildCmdDeviceExecutionList(), - buildCmdDeviceExecutionInspect(), + buildCmdDeviceExecutionGet(), }, } } diff --git a/internal/app/enaptercli/cmd_device_execution_inspect.go b/internal/app/enaptercli/cmd_device_execution_get.go similarity index 73% rename from internal/app/enaptercli/cmd_device_execution_inspect.go rename to internal/app/enaptercli/cmd_device_execution_get.go index 04ff3fb..e597ce4 100644 --- a/internal/app/enaptercli/cmd_device_execution_inspect.go +++ b/internal/app/enaptercli/cmd_device_execution_get.go @@ -9,17 +9,17 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDeviceExecutionInspect struct { +type cmdDeviceExecutionGet struct { cmdDeviceExecution executionID string expand []string } -func buildCmdDeviceExecutionInspect() *cli.Command { - cmd := &cmdDeviceExecutionInspect{} +func buildCmdDeviceExecutionGet() *cli.Command { + cmd := &cmdDeviceExecutionGet{} return &cli.Command{ - Name: "inspect", - Usage: "Inspect a device command execution", + Name: "get", + Usage: "Get a device command execution", CustomHelpTemplate: cmd.HelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, @@ -29,7 +29,7 @@ func buildCmdDeviceExecutionInspect() *cli.Command { } } -func (c *cmdDeviceExecutionInspect) Flags() []cli.Flag { +func (c *cmdDeviceExecutionGet) Flags() []cli.Flag { flags := c.cmdDeviceExecution.Flags() return append(flags, &cli.StringFlag{ @@ -47,14 +47,14 @@ func (c *cmdDeviceExecutionInspect) Flags() []cli.Flag { ) } -func (c *cmdDeviceExecutionInspect) Before(cliCtx *cli.Context) error { +func (c *cmdDeviceExecutionGet) Before(cliCtx *cli.Context) error { if err := c.cmdDevices.Before(cliCtx); err != nil { return err } return validateExpandFlag(cliCtx, []string{"log"}) } -func (c *cmdDeviceExecutionInspect) do(ctx context.Context) error { +func (c *cmdDeviceExecutionGet) do(ctx context.Context) error { query := url.Values{} if len(c.expand) != 0 { query.Set("expand", strings.Join(c.expand, ",")) diff --git a/internal/app/enaptercli/cmd_device_inspect.go b/internal/app/enaptercli/cmd_device_get.go similarity index 75% rename from internal/app/enaptercli/cmd_device_inspect.go rename to internal/app/enaptercli/cmd_device_get.go index 4074829..94b5917 100644 --- a/internal/app/enaptercli/cmd_device_inspect.go +++ b/internal/app/enaptercli/cmd_device_get.go @@ -9,17 +9,17 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDevicesInspect struct { +type cmdDevicesGet struct { cmdDevices deviceID string expand []string } -func buildCmdDevicesInspect() *cli.Command { - cmd := &cmdDevicesInspect{} +func buildCmdDevicesGet() *cli.Command { + cmd := &cmdDevicesGet{} return &cli.Command{ - Name: "inspect", - Usage: "Inspect a devices", + Name: "get", + Usage: "Get a device info", CustomHelpTemplate: cmd.HelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, @@ -29,7 +29,7 @@ func buildCmdDevicesInspect() *cli.Command { } } -func (c *cmdDevicesInspect) Flags() []cli.Flag { +func (c *cmdDevicesGet) Flags() []cli.Flag { flags := c.cmdDevices.Flags() return append(flags, &cli.StringFlag{ Name: "device-id", @@ -46,14 +46,14 @@ func (c *cmdDevicesInspect) Flags() []cli.Flag { }) } -func (c *cmdDevicesInspect) Before(cliCtx *cli.Context) error { +func (c *cmdDevicesGet) Before(cliCtx *cli.Context) error { if err := c.cmdDevices.Before(cliCtx); err != nil { return err } return c.validateExpandFlag(cliCtx) } -func (c *cmdDevicesInspect) do(ctx context.Context) error { +func (c *cmdDevicesGet) do(ctx context.Context) error { query := url.Values{} if len(c.expand) != 0 { query.Set("expand", strings.Join(c.expand, ",")) diff --git a/internal/app/enaptercli/cmd_rule_engine.go b/internal/app/enaptercli/cmd_rule_engine.go index f611e08..9152991 100644 --- a/internal/app/enaptercli/cmd_rule_engine.go +++ b/internal/app/enaptercli/cmd_rule_engine.go @@ -17,7 +17,7 @@ func buildCmdRuleEngine() *cli.Command { Name: "rule-engine", Usage: "Manage the rule engine", Subcommands: []*cli.Command{ - buildCmdRuleEngineInspect(), + buildCmdRuleEngineGet(), buildCmdRuleEngineSuspend(), buildCmdRuleEngineResume(), buildCmdRuleEngineRule(), diff --git a/internal/app/enaptercli/cmd_rule_engine_inspect.go b/internal/app/enaptercli/cmd_rule_engine_get.go similarity index 61% rename from internal/app/enaptercli/cmd_rule_engine_inspect.go rename to internal/app/enaptercli/cmd_rule_engine_get.go index 304153c..7ff0479 100644 --- a/internal/app/enaptercli/cmd_rule_engine_inspect.go +++ b/internal/app/enaptercli/cmd_rule_engine_get.go @@ -7,15 +7,15 @@ import ( "github.com/urfave/cli/v2" ) -type cmdRuleEngineInspect struct { +type cmdRuleEngineGet struct { cmdRuleEngine } -func buildCmdRuleEngineInspect() *cli.Command { - cmd := &cmdRuleEngineInspect{} +func buildCmdRuleEngineGet() *cli.Command { + cmd := &cmdRuleEngineGet{} return &cli.Command{ - Name: "inspect", - Usage: "Inspect the rule engine", + Name: "get", + Usage: "Get the rule engine", CustomHelpTemplate: cmd.HelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, @@ -25,7 +25,7 @@ func buildCmdRuleEngineInspect() *cli.Command { } } -func (c *cmdRuleEngineInspect) do(ctx context.Context) error { +func (c *cmdRuleEngineGet) do(ctx context.Context) error { return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, Path: "", diff --git a/internal/app/enaptercli/cmd_rule_engine_rule.go b/internal/app/enaptercli/cmd_rule_engine_rule.go index 916789b..2c209cf 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule.go @@ -26,7 +26,7 @@ func buildCmdRuleEngineRule() *cli.Command { buildCmdRuleEngineRuleDelete(), buildCmdRuleEngineRuleDisable(), buildCmdRuleEngineRuleEnable(), - buildCmdRuleEngineRuleInspect(), + buildCmdRuleEngineRuleGet(), buildCmdRuleEngineRuleList(), buildCmdRuleEngineRuleUpdate(), buildCmdRuleEngineRuleUpdateScript(), diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_inspect.go b/internal/app/enaptercli/cmd_rule_engine_rule_get.go similarity index 66% rename from internal/app/enaptercli/cmd_rule_engine_rule_inspect.go rename to internal/app/enaptercli/cmd_rule_engine_rule_get.go index 91a0527..a637694 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_inspect.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_get.go @@ -7,16 +7,16 @@ import ( "github.com/urfave/cli/v2" ) -type cmdRuleEngineRuleInspect struct { +type cmdRuleEngineRuleGet struct { cmdRuleEngineRule ruleID string } -func buildCmdRuleEngineRuleInspect() *cli.Command { - cmd := &cmdRuleEngineRuleInspect{} +func buildCmdRuleEngineRuleGet() *cli.Command { + cmd := &cmdRuleEngineRuleGet{} return &cli.Command{ - Name: "inspect", - Usage: "Inspect a rule", + Name: "get", + Usage: "Get a rule", CustomHelpTemplate: cmd.HelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, @@ -26,7 +26,7 @@ func buildCmdRuleEngineRuleInspect() *cli.Command { } } -func (c *cmdRuleEngineRuleInspect) Flags() []cli.Flag { +func (c *cmdRuleEngineRuleGet) Flags() []cli.Flag { return append(c.cmdRuleEngineRule.Flags(), &cli.StringFlag{ Name: "rule-id", @@ -37,7 +37,7 @@ func (c *cmdRuleEngineRuleInspect) Flags() []cli.Flag { ) } -func (c *cmdRuleEngineRuleInspect) do(ctx context.Context) error { +func (c *cmdRuleEngineRuleGet) do(ctx context.Context) error { return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, Path: "/" + c.ruleID, diff --git a/internal/app/enaptercli/cmd_site.go b/internal/app/enaptercli/cmd_site.go index b387ad0..2755ebf 100644 --- a/internal/app/enaptercli/cmd_site.go +++ b/internal/app/enaptercli/cmd_site.go @@ -18,7 +18,7 @@ func buildCmdSites() *cli.Command { Usage: "Manage sites", Subcommands: []*cli.Command{ buildCmdSitesList(), - buildCmdSiteInspect(), + buildCmdSiteGet(), }, } } diff --git a/internal/app/enaptercli/cmd_site_inspect.go b/internal/app/enaptercli/cmd_site_get.go similarity index 69% rename from internal/app/enaptercli/cmd_site_inspect.go rename to internal/app/enaptercli/cmd_site_get.go index 32db8d9..d32b7a1 100644 --- a/internal/app/enaptercli/cmd_site_inspect.go +++ b/internal/app/enaptercli/cmd_site_get.go @@ -7,16 +7,16 @@ import ( "github.com/urfave/cli/v2" ) -type cmdSiteInspect struct { +type cmdSiteGet struct { cmdSite siteID string } -func buildCmdSiteInspect() *cli.Command { - cmd := &cmdSiteInspect{} +func buildCmdSiteGet() *cli.Command { + cmd := &cmdSiteGet{} return &cli.Command{ - Name: "inspect", - Usage: "Inspect a site", + Name: "get", + Usage: "Get a site", CustomHelpTemplate: cmd.HelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, @@ -26,7 +26,7 @@ func buildCmdSiteInspect() *cli.Command { } } -func (c *cmdSiteInspect) Flags() []cli.Flag { +func (c *cmdSiteGet) Flags() []cli.Flag { flags := c.cmdSite.Flags() return append(flags, &cli.StringFlag{ Name: "site-id", @@ -36,7 +36,7 @@ func (c *cmdSiteInspect) Flags() []cli.Flag { }) } -func (c *cmdSiteInspect) do(ctx context.Context) error { +func (c *cmdSiteGet) do(ctx context.Context) error { return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, Path: "/" + c.siteID, diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint b/internal/app/enaptercli/testdata/helps/enapter blueprint index 367e380..01e01dc 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint @@ -8,7 +8,7 @@ COMMANDS: profiles Manage blueprints profiles upload Upload blueprint into Platform download Download blueprint zip from Platform - inspect Get blueprint metainfo + get Get blueprint metadata help, h Shows a list of commands or help for one command OPTIONS: diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint inspect b/internal/app/enaptercli/testdata/helps/enapter blueprint get similarity index 62% rename from internal/app/enaptercli/testdata/helps/enapter blueprint inspect rename to internal/app/enaptercli/testdata/helps/enapter blueprint get index 2309ca4..f2c839e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint inspect +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint get @@ -1,12 +1,12 @@ NAME: - enaptercli.test blueprint inspect - Get blueprint metainfo + enaptercli.test blueprint get - Get blueprint metadata USAGE: - enaptercli.test blueprint inspect [command options] + enaptercli.test blueprint get [command options] OPTIONS: --verbose log extra details about operation (default: false) - --blueprint-id value, -b value blueprint name or ID to inspect + --blueprint-id value, -b value blueprint name or ID to get --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index be1def9..08f896c 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -6,7 +6,7 @@ USAGE: COMMANDS: list List user devices - inspect Inspect a devices + get Get a device info assign-blueprint Assign blueprint to device logs Show device logs delete Delete a device diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution b/internal/app/enaptercli/testdata/helps/enapter device execution index ed07025..e7ff6bd 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execution +++ b/internal/app/enaptercli/testdata/helps/enapter device execution @@ -6,7 +6,7 @@ USAGE: COMMANDS: list List device command executions - inspect Inspect a device command execution + get Get a device command execution help, h Shows a list of commands or help for one command OPTIONS: diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution inspect b/internal/app/enaptercli/testdata/helps/enapter device execution get similarity index 77% rename from internal/app/enaptercli/testdata/helps/enapter device execution inspect rename to internal/app/enaptercli/testdata/helps/enapter device execution get index 4069676..777edf1 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execution inspect +++ b/internal/app/enaptercli/testdata/helps/enapter device execution get @@ -1,8 +1,8 @@ NAME: - enaptercli.test device execution inspect - Inspect a device command execution + enaptercli.test device execution get - Get a device command execution USAGE: - enaptercli.test device execution inspect [command options] + enaptercli.test device execution get [command options] OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device inspect b/internal/app/enaptercli/testdata/helps/enapter device get similarity index 80% rename from internal/app/enaptercli/testdata/helps/enapter device inspect rename to internal/app/enaptercli/testdata/helps/enapter device get index f932da4..a6dec33 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device inspect +++ b/internal/app/enaptercli/testdata/helps/enapter device get @@ -1,8 +1,8 @@ NAME: - enaptercli.test device inspect - Inspect a devices + enaptercli.test device get - Get a device info USAGE: - enaptercli.test device inspect [command options] + enaptercli.test device get [command options] OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine b/internal/app/enaptercli/testdata/helps/enapter rule-engine index 4afdb77..27613bb 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine @@ -5,7 +5,7 @@ USAGE: enaptercli.test rule-engine command [command options] COMMANDS: - inspect Inspect the rule engine + get Get the rule engine suspend Suspend execution of rules resume Resume execution of rules rule Manage rules diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine inspect b/internal/app/enaptercli/testdata/helps/enapter rule-engine get similarity index 68% rename from internal/app/enaptercli/testdata/helps/enapter rule-engine inspect rename to internal/app/enaptercli/testdata/helps/enapter rule-engine get index 0abc976..c227429 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine inspect +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine get @@ -1,8 +1,8 @@ NAME: - enaptercli.test rule-engine inspect - Inspect the rule engine + enaptercli.test rule-engine get - Get the rule engine USAGE: - enaptercli.test rule-engine inspect [command options] + enaptercli.test rule-engine get [command options] OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule index e37f204..c75e7b6 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule @@ -9,7 +9,7 @@ COMMANDS: delete Delete one or more rules disable Disable one or more rules enable Enable one or more rules - inspect Inspect a rule + get Get a rule list List rules update Update a rule update-script Update the script of a rule diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule inspect b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get similarity index 71% rename from internal/app/enaptercli/testdata/helps/enapter rule-engine rule inspect rename to internal/app/enaptercli/testdata/helps/enapter rule-engine rule get index a829366..51d7ff7 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule inspect +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get @@ -1,8 +1,8 @@ NAME: - enaptercli.test rule-engine rule inspect - Inspect a rule + enaptercli.test rule-engine rule get - Get a rule USAGE: - enaptercli.test rule-engine rule inspect [command options] + enaptercli.test rule-engine rule get [command options] OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter site b/internal/app/enaptercli/testdata/helps/enapter site index 481d79f..59f088a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site +++ b/internal/app/enaptercli/testdata/helps/enapter site @@ -6,7 +6,7 @@ USAGE: COMMANDS: list List user sites - inspect Inspect a site + get Get a site help, h Shows a list of commands or help for one command OPTIONS: diff --git a/internal/app/enaptercli/testdata/helps/enapter site inspect b/internal/app/enaptercli/testdata/helps/enapter site get similarity index 75% rename from internal/app/enaptercli/testdata/helps/enapter site inspect rename to internal/app/enaptercli/testdata/helps/enapter site get index 6cc9598..79ca102 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site inspect +++ b/internal/app/enaptercli/testdata/helps/enapter site get @@ -1,8 +1,8 @@ NAME: - enaptercli.test site inspect - Inspect a site + enaptercli.test site get - Get a site USAGE: - enaptercli.test site inspect [command options] + enaptercli.test site get [command options] OPTIONS: --verbose log extra details about operation (default: false) diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl index 1c4cc59..575c1d6 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl @@ -1 +1 @@ -enapter3 blueprint inspect {{.BaseFlags}} --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 +enapter3 blueprint get {{.BaseFlags}} --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl index 5b9d1a7..9a7c247 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl @@ -1 +1 @@ -enapter3 blueprint inspect {{.BaseFlags}} --blueprint-id test_blueprint_name +enapter3 blueprint get {{.BaseFlags}} --blueprint-id test_blueprint_name From 7fa43a28ff332a35c37878345e49725a3df60f62 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 27 Jun 2025 15:39:18 +0300 Subject: [PATCH 39/88] feat: rename api-host to api-url and make it visible option --- internal/app/enaptercli/cmd_base.go | 11 ++++------- internal/app/enaptercli/execute_test.go | 2 +- .../testdata/helps/enapter blueprint download | 3 ++- .../enaptercli/testdata/helps/enapter blueprint get | 3 ++- .../helps/enapter blueprint profiles download | 3 ++- .../testdata/helps/enapter blueprint profiles upload | 3 ++- .../testdata/helps/enapter blueprint upload | 3 ++- .../testdata/helps/enapter device assign-blueprint | 3 ++- .../enaptercli/testdata/helps/enapter device delete | 3 ++- .../testdata/helps/enapter device execute-command | 3 ++- .../testdata/helps/enapter device execution get | 3 ++- .../testdata/helps/enapter device execution list | 3 ++- .../app/enaptercli/testdata/helps/enapter device get | 3 ++- .../app/enaptercli/testdata/helps/enapter device list | 3 ++- .../app/enaptercli/testdata/helps/enapter device logs | 3 ++- .../testdata/helps/enapter provisioning lua-device | 3 ++- .../testdata/helps/enapter provisioning standalone | 3 ++- .../enaptercli/testdata/helps/enapter rule-engine get | 7 ++++--- .../testdata/helps/enapter rule-engine resume | 7 ++++--- .../testdata/helps/enapter rule-engine rule create | 3 ++- .../testdata/helps/enapter rule-engine rule delete | 3 ++- .../testdata/helps/enapter rule-engine rule disable | 3 ++- .../testdata/helps/enapter rule-engine rule enable | 3 ++- .../testdata/helps/enapter rule-engine rule get | 3 ++- .../testdata/helps/enapter rule-engine rule list | 7 ++++--- .../testdata/helps/enapter rule-engine rule logs | 3 ++- .../testdata/helps/enapter rule-engine rule update | 3 ++- .../helps/enapter rule-engine rule update-script | 3 ++- .../testdata/helps/enapter rule-engine suspend | 7 ++++--- .../app/enaptercli/testdata/helps/enapter site get | 3 ++- .../app/enaptercli/testdata/helps/enapter site list | 7 ++++--- 31 files changed, 73 insertions(+), 47 deletions(-) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 3a60a3b..120987d 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -34,16 +34,13 @@ func (c *cmdBase) Flags() []cli.Flag { EnvVars: []string{"ENAPTER3_API_TOKEN"}, Hidden: true, Destination: &c.token, - Category: "HTTP API Configuration:", }, &cli.StringFlag{ - Name: "api-host", - Usage: "Override API endpoint", - EnvVars: []string{"ENAPTER3_API_HOST"}, - Hidden: true, + Name: "api-url", + Usage: "override API base URL", + EnvVars: []string{"ENAPTER3_API_URL"}, Value: "https://api.enapter.com", Destination: &c.apiHost, - Category: "HTTP API Configuration:", Action: func(_ *cli.Context, v string) error { c.apiHost = strings.TrimSuffix(v, "/") return nil @@ -70,7 +67,7 @@ func (c *cmdBase) HelpTemplate() string { return cli.CommandHelpTemplate + ` ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) ` } diff --git a/internal/app/enaptercli/execute_test.go b/internal/app/enaptercli/execute_test.go index 9e2d0fe..733c866 100644 --- a/internal/app/enaptercli/execute_test.go +++ b/internal/app/enaptercli/execute_test.go @@ -83,7 +83,7 @@ func TestHTTPReqResp(t *testing.T) { defer srv.Close() tmplParams := struct{ BaseFlags string }{ - BaseFlags: strings.Join([]string{"--token", testToken, "--api-host", srv.URL}, " "), + BaseFlags: strings.Join([]string{"--token", testToken, "--api-url", srv.URL}, " "), } cmd := executeTmpl(t, filepath.Join(testdataPath, tc.Name(), "cmd.tmpl"), tmplParams) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint download b/internal/app/enaptercli/testdata/helps/enapter blueprint download index 113afb9..06be670 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint download @@ -5,6 +5,7 @@ USAGE: enaptercli.test blueprint download [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --blueprint-id value, -b value blueprint name or ID to download --output value, -o value blueprint file name to save @@ -12,5 +13,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint get b/internal/app/enaptercli/testdata/helps/enapter blueprint get index f2c839e..a383f95 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint get +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint get @@ -5,11 +5,12 @@ USAGE: enaptercli.test blueprint get [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --blueprint-id value, -b value blueprint name or ID to get --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download index 391a1b0..6f730e2 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download @@ -5,11 +5,12 @@ USAGE: enaptercli.test blueprint profiles download [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --output value, -o value file name to save --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload index 598c1a2..83c5739 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload @@ -5,11 +5,12 @@ USAGE: enaptercli.test blueprint profiles upload [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --profiles-path value, -p value profiles zip file path --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint upload b/internal/app/enaptercli/testdata/helps/enapter blueprint upload index 0e7e170..02b46c8 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint upload @@ -5,11 +5,12 @@ USAGE: enaptercli.test blueprint upload [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --path value, -p value blueprint path (zip file or directory) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint b/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint index 675510e..6f7fbe2 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint @@ -5,6 +5,7 @@ USAGE: enaptercli.test device assign-blueprint [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --device-id value, -d value device ID --blueprint-id value, -b value blueprint ID to assign @@ -13,5 +14,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter device delete b/internal/app/enaptercli/testdata/helps/enapter device delete index da762be..1a78ae3 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device delete +++ b/internal/app/enaptercli/testdata/helps/enapter device delete @@ -5,11 +5,12 @@ USAGE: enaptercli.test device delete [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --device-id value device ID --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter device execute-command b/internal/app/enaptercli/testdata/helps/enapter device execute-command index a808cc6..58c7701 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execute-command +++ b/internal/app/enaptercli/testdata/helps/enapter device execute-command @@ -5,6 +5,7 @@ USAGE: enaptercli.test device execute-command [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --device-id value device ID --cmd-name value command name @@ -14,5 +15,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution get b/internal/app/enaptercli/testdata/helps/enapter device execution get index 777edf1..f6d30af 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execution get +++ b/internal/app/enaptercli/testdata/helps/enapter device execution get @@ -5,6 +5,7 @@ USAGE: enaptercli.test device execution get [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --device-id value, -d value device ID --execution-id value execution ID @@ -13,5 +14,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution list b/internal/app/enaptercli/testdata/helps/enapter device execution list index 80bbe21..f417754 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execution list +++ b/internal/app/enaptercli/testdata/helps/enapter device execution list @@ -5,6 +5,7 @@ USAGE: enaptercli.test device execution list [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --device-id value, -d value device ID --expand value [ --expand value ] coma separated list of expanded options @@ -12,5 +13,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter device get b/internal/app/enaptercli/testdata/helps/enapter device get index a6dec33..fc52ca0 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device get +++ b/internal/app/enaptercli/testdata/helps/enapter device get @@ -5,6 +5,7 @@ USAGE: enaptercli.test device get [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --device-id value, -d value device ID --expand value [ --expand value ] coma separated list of expanded device info @@ -12,5 +13,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter device list b/internal/app/enaptercli/testdata/helps/enapter device list index be0255c..bb3fc23 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device list +++ b/internal/app/enaptercli/testdata/helps/enapter device list @@ -5,6 +5,7 @@ USAGE: enaptercli.test device list [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --expand value [ --expand value ] coma separated list of expanded device info --site-id value list devices from this site @@ -12,5 +13,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter device logs b/internal/app/enaptercli/testdata/helps/enapter device logs index 09ac0f5..338d05d 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device logs +++ b/internal/app/enaptercli/testdata/helps/enapter device logs @@ -5,6 +5,7 @@ USAGE: enaptercli.test device logs [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --device-id value, -d value device ID --follow, -f follow log output (default: false) @@ -19,5 +20,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device b/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device index e7d175c..d7fd8ad 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device @@ -5,6 +5,7 @@ USAGE: enaptercli.test provisioning lua-device [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --runtime-id value, -r value runtime UCM device ID where to run a new Lua device --device-name value, -n value name of a new Lua device @@ -15,5 +16,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone index e8ce45f..94d10df 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone @@ -5,6 +5,7 @@ USAGE: enaptercli.test provisioning standalone [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --site-id value, -s value site ID where to craate device --device-name value, -n value name for a new device @@ -12,5 +13,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine get b/internal/app/enaptercli/testdata/helps/enapter rule-engine get index c227429..b3ff2ef 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine get +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine get @@ -5,10 +5,11 @@ USAGE: enaptercli.test rule-engine get [command options] OPTIONS: - --verbose log extra details about operation (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --verbose log extra details about operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume index ba1032b..a7f2203 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume @@ -5,10 +5,11 @@ USAGE: enaptercli.test rule-engine resume [command options] OPTIONS: - --verbose log extra details about operation (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --verbose log extra details about operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create index 7f56691..5e7650a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine rule create [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --slug value Slug of a new rule --script value Path to a file containing the script code @@ -15,5 +16,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete index 11e1f56..4ef74dd 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete @@ -5,11 +5,12 @@ USAGE: enaptercli.test rule-engine rule delete [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable index 0aa5581..9ee075d 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable @@ -5,11 +5,12 @@ USAGE: enaptercli.test rule-engine rule disable [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable index 487c801..5e513e8 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable @@ -5,11 +5,12 @@ USAGE: enaptercli.test rule-engine rule enable [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get index 51d7ff7..0314b47 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get @@ -5,11 +5,12 @@ USAGE: enaptercli.test rule-engine rule get [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --rule-id value Rule ID or slug --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list index 7f72e22..ba34f6f 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list @@ -5,10 +5,11 @@ USAGE: enaptercli.test rule-engine rule list [command options] OPTIONS: - --verbose log extra details about operation (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --verbose log extra details about operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs index f058d75..85631c3 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine rule logs [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --rule-id value rule ID --follow, -f follow log output (default: false) @@ -12,5 +13,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update index 100a633..707505d 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine rule update [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --rule-id value Rule ID or slug to update --slug value A new rule slug @@ -12,5 +13,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script index 6b421da..977d825 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script @@ -5,6 +5,7 @@ USAGE: enaptercli.test rule-engine rule update-script [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --rule-id value Rule ID or slug to update --script value Path to a file containing the script code @@ -14,5 +15,5 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend index 3f5f4ae..7391d93 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend @@ -5,10 +5,11 @@ USAGE: enaptercli.test rule-engine suspend [command options] OPTIONS: - --verbose log extra details about operation (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --verbose log extra details about operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter site get b/internal/app/enaptercli/testdata/helps/enapter site get index 79ca102..6dc4f57 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site get +++ b/internal/app/enaptercli/testdata/helps/enapter site get @@ -5,11 +5,12 @@ USAGE: enaptercli.test site get [command options] OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --verbose log extra details about operation (default: false) --site-id value site ID --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) diff --git a/internal/app/enaptercli/testdata/helps/enapter site list b/internal/app/enaptercli/testdata/helps/enapter site list index 34b220a..414bb97 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site list +++ b/internal/app/enaptercli/testdata/helps/enapter site list @@ -5,10 +5,11 @@ USAGE: enaptercli.test site list [command options] OPTIONS: - --verbose log extra details about operation (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --verbose log extra details about operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_HOST Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) From 616f5dbce0d92e0009722e3beb13ed6bbd6f2073 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 27 Jun 2025 16:02:22 +0300 Subject: [PATCH 40/88] feat: allow insecure http connection to api --- internal/app/enaptercli/cmd_base.go | 31 ++++++++++++++----- .../testdata/helps/enapter blueprint download | 6 ++-- .../testdata/helps/enapter blueprint get | 6 ++-- .../helps/enapter blueprint profiles download | 6 ++-- .../helps/enapter blueprint profiles upload | 6 ++-- .../testdata/helps/enapter blueprint upload | 6 ++-- .../helps/enapter device assign-blueprint | 6 ++-- .../testdata/helps/enapter device delete | 14 +++++---- .../helps/enapter device execute-command | 20 ++++++------ .../helps/enapter device execution get | 6 ++-- .../helps/enapter device execution list | 6 ++-- .../testdata/helps/enapter device get | 6 ++-- .../testdata/helps/enapter device list | 6 ++-- .../testdata/helps/enapter device logs | 6 ++-- .../helps/enapter provisioning lua-device | 6 ++-- .../helps/enapter provisioning standalone | 6 ++-- .../testdata/helps/enapter rule-engine get | 12 ++++--- .../testdata/helps/enapter rule-engine resume | 12 ++++--- .../helps/enapter rule-engine rule create | 6 ++-- .../helps/enapter rule-engine rule delete | 6 ++-- .../helps/enapter rule-engine rule disable | 6 ++-- .../helps/enapter rule-engine rule enable | 6 ++-- .../helps/enapter rule-engine rule get | 14 +++++---- .../helps/enapter rule-engine rule list | 12 ++++--- .../helps/enapter rule-engine rule logs | 16 +++++----- .../helps/enapter rule-engine rule update | 16 +++++----- .../enapter rule-engine rule update-script | 6 ++-- .../helps/enapter rule-engine suspend | 12 ++++--- .../testdata/helps/enapter site get | 14 +++++---- .../testdata/helps/enapter site list | 12 ++++--- 30 files changed, 183 insertions(+), 110 deletions(-) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 120987d..9f9808f 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -3,6 +3,7 @@ package enaptercli import ( "bytes" "context" + "crypto/tls" "encoding/base64" "encoding/json" "errors" @@ -19,11 +20,12 @@ import ( ) type cmdBase struct { - verbose bool - token string - apiHost string - writer io.Writer - httpClient *http.Client + verbose bool + token string + apiHost string + apiAllowInsecure bool + writer io.Writer + httpClient *http.Client } func (c *cmdBase) Flags() []cli.Flag { @@ -46,6 +48,12 @@ func (c *cmdBase) Flags() []cli.Flag { return nil }, }, + &cli.BoolFlag{ + Name: "api-allow-insecure", + Usage: "allow insecure connections to Enapter API", + EnvVars: []string{"ENAPTER3_API_ALLOW_INSECURE"}, + Destination: &c.apiAllowInsecure, + }, &cli.BoolFlag{ Name: "verbose", Usage: "log extra details about operation", @@ -59,15 +67,22 @@ func (c *cmdBase) Before(cliCtx *cli.Context) error { return errAPITokenMissed } c.writer = cliCtx.App.Writer - c.httpClient = http.DefaultClient + c.httpClient = &http.Client{ + Transport: &http.Transport{ + //nolint:gosec // This is needed to allow self-signed certificates on Gateway. + TLSClientConfig: &tls.Config{InsecureSkipVerify: c.apiAllowInsecure}, + }, + } + return nil } func (c *cmdBase) HelpTemplate() string { return cli.CommandHelpTemplate + ` ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) ` } diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint download b/internal/app/enaptercli/testdata/helps/enapter blueprint download index 06be670..b298078 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint download @@ -6,12 +6,14 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --blueprint-id value, -b value blueprint name or ID to download --output value, -o value blueprint file name to save --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint get b/internal/app/enaptercli/testdata/helps/enapter blueprint get index a383f95..3a6cc02 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint get +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint get @@ -6,11 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --blueprint-id value, -b value blueprint name or ID to get --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download index 6f730e2..b6dcf1b 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download @@ -6,11 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --output value, -o value file name to save --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload index 83c5739..7e30805 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload @@ -6,11 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --profiles-path value, -p value profiles zip file path --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint upload b/internal/app/enaptercli/testdata/helps/enapter blueprint upload index 02b46c8..eefa2ba 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint upload @@ -6,11 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --path value, -p value blueprint path (zip file or directory) --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint b/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint index 6f7fbe2..f6f6edc 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint @@ -6,6 +6,7 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --device-id value, -d value device ID --blueprint-id value, -b value blueprint ID to assign @@ -13,6 +14,7 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device delete b/internal/app/enaptercli/testdata/helps/enapter device delete index 1a78ae3..a9c1e8a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device delete +++ b/internal/app/enaptercli/testdata/helps/enapter device delete @@ -5,12 +5,14 @@ USAGE: enaptercli.test device delete [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --verbose log extra details about operation (default: false) - --device-id value device ID - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --device-id value device ID + --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device execute-command b/internal/app/enaptercli/testdata/helps/enapter device execute-command index 58c7701..4181a1e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execute-command +++ b/internal/app/enaptercli/testdata/helps/enapter device execute-command @@ -5,15 +5,17 @@ USAGE: enaptercli.test device execute-command [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --verbose log extra details about operation (default: false) - --device-id value device ID - --cmd-name value command name - --cmd-args value command args (should be a JSON string) - --ephemeral run command in ephemeral mode (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --device-id value device ID + --cmd-name value command name + --cmd-args value command args (should be a JSON string) + --ephemeral run command in ephemeral mode (default: false) + --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution get b/internal/app/enaptercli/testdata/helps/enapter device execution get index f6d30af..610b674 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execution get +++ b/internal/app/enaptercli/testdata/helps/enapter device execution get @@ -6,6 +6,7 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --device-id value, -d value device ID --execution-id value execution ID @@ -13,6 +14,7 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution list b/internal/app/enaptercli/testdata/helps/enapter device execution list index f417754..25d7cfb 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execution list +++ b/internal/app/enaptercli/testdata/helps/enapter device execution list @@ -6,12 +6,14 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --device-id value, -d value device ID --expand value [ --expand value ] coma separated list of expanded options --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device get b/internal/app/enaptercli/testdata/helps/enapter device get index fc52ca0..43b24ed 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device get +++ b/internal/app/enaptercli/testdata/helps/enapter device get @@ -6,12 +6,14 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --device-id value, -d value device ID --expand value [ --expand value ] coma separated list of expanded device info --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device list b/internal/app/enaptercli/testdata/helps/enapter device list index bb3fc23..f7951a0 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device list +++ b/internal/app/enaptercli/testdata/helps/enapter device list @@ -6,12 +6,14 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --expand value [ --expand value ] coma separated list of expanded device info --site-id value list devices from this site --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device logs b/internal/app/enaptercli/testdata/helps/enapter device logs index 338d05d..27c6288 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device logs +++ b/internal/app/enaptercli/testdata/helps/enapter device logs @@ -6,6 +6,7 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --device-id value, -d value device ID --follow, -f follow log output (default: false) @@ -19,6 +20,7 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device b/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device index d7fd8ad..976cbb4 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device @@ -6,6 +6,7 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --runtime-id value, -r value runtime UCM device ID where to run a new Lua device --device-name value, -n value name of a new Lua device @@ -15,6 +16,7 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone index 94d10df..00c0d34 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone @@ -6,12 +6,14 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --site-id value, -s value site ID where to craate device --device-name value, -n value name for a new device --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine get b/internal/app/enaptercli/testdata/helps/enapter rule-engine get index b3ff2ef..b427e6f 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine get +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine get @@ -5,11 +5,13 @@ USAGE: enaptercli.test rule-engine get [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --verbose log extra details about operation (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume index a7f2203..9994e7e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume @@ -5,11 +5,13 @@ USAGE: enaptercli.test rule-engine resume [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --verbose log extra details about operation (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create index 5e7650a..8af3d12 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create @@ -6,6 +6,7 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --slug value Slug of a new rule --script value Path to a file containing the script code @@ -15,6 +16,7 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete index 4ef74dd..7d4de28 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete @@ -6,11 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable index 9ee075d..d34f98f 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable @@ -6,11 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable index 5e513e8..bee9481 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable @@ -6,11 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get index 0314b47..8b2e07f 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get @@ -5,12 +5,14 @@ USAGE: enaptercli.test rule-engine rule get [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --verbose log extra details about operation (default: false) - --rule-id value Rule ID or slug - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --rule-id value Rule ID or slug + --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list index ba34f6f..675f647 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list @@ -5,11 +5,13 @@ USAGE: enaptercli.test rule-engine rule list [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --verbose log extra details about operation (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs index 85631c3..1d2a4a0 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs @@ -5,13 +5,15 @@ USAGE: enaptercli.test rule-engine rule logs [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --verbose log extra details about operation (default: false) - --rule-id value rule ID - --follow, -f follow log output (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --rule-id value rule ID + --follow, -f follow log output (default: false) + --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update index 707505d..b5a5aae 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update @@ -5,13 +5,15 @@ USAGE: enaptercli.test rule-engine rule update [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --verbose log extra details about operation (default: false) - --rule-id value Rule ID or slug to update - --slug value A new rule slug - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --rule-id value Rule ID or slug to update + --slug value A new rule slug + --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script index 977d825..8009d49 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script @@ -6,6 +6,7 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --rule-id value Rule ID or slug to update --script value Path to a file containing the script code @@ -14,6 +15,7 @@ OPTIONS: --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend index 7391d93..282117b 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend @@ -5,11 +5,13 @@ USAGE: enaptercli.test rule-engine suspend [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --verbose log extra details about operation (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter site get b/internal/app/enaptercli/testdata/helps/enapter site get index 6dc4f57..6d61a7e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site get +++ b/internal/app/enaptercli/testdata/helps/enapter site get @@ -5,12 +5,14 @@ USAGE: enaptercli.test site get [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --verbose log extra details about operation (default: false) - --site-id value site ID - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --site-id value site ID + --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) diff --git a/internal/app/enaptercli/testdata/helps/enapter site list b/internal/app/enaptercli/testdata/helps/enapter site list index 414bb97..c4adbe6 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site list +++ b/internal/app/enaptercli/testdata/helps/enapter site list @@ -5,11 +5,13 @@ USAGE: enaptercli.test site list [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --verbose log extra details about operation (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: - ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) From f794362add10ccc1ef9af4ee34fba4ceac6ab6d0 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 27 Jun 2025 17:08:55 +0300 Subject: [PATCH 41/88] feat: add ENVIRONMENT VARIABLES to all helps --- internal/app/enaptercli/cmd_base.go | 10 ++++++++-- internal/app/enaptercli/cmd_blueprints.go | 6 ++++-- .../app/enaptercli/cmd_blueprints_download.go | 2 +- internal/app/enaptercli/cmd_blueprints_get.go | 2 +- .../app/enaptercli/cmd_blueprints_profiles.go | 6 ++++-- .../cmd_blueprints_profiles_download.go | 2 +- .../cmd_blueprints_profiles_upload.go | 2 +- .../app/enaptercli/cmd_blueprints_upload.go | 2 +- internal/app/enaptercli/cmd_device.go | 6 ++++-- .../enaptercli/cmd_device_assign_blueprint.go | 2 +- internal/app/enaptercli/cmd_device_delete.go | 2 +- .../enaptercli/cmd_device_execute_command.go | 2 +- .../app/enaptercli/cmd_device_execution.go | 6 ++++-- .../enaptercli/cmd_device_execution_get.go | 2 +- .../enaptercli/cmd_device_execution_list.go | 2 +- internal/app/enaptercli/cmd_device_get.go | 2 +- internal/app/enaptercli/cmd_device_list.go | 2 +- internal/app/enaptercli/cmd_device_logs.go | 2 +- internal/app/enaptercli/cmd_provisioning.go | 6 ++++-- .../enaptercli/cmd_provisioning_lua_device.go | 2 +- .../enaptercli/cmd_provisioning_standalone.go | 2 +- internal/app/enaptercli/cmd_rule_engine.go | 6 ++++-- .../app/enaptercli/cmd_rule_engine_get.go | 2 +- .../app/enaptercli/cmd_rule_engine_resume.go | 2 +- .../app/enaptercli/cmd_rule_engine_rule.go | 6 ++++-- .../enaptercli/cmd_rule_engine_rule_create.go | 2 +- .../enaptercli/cmd_rule_engine_rule_delete.go | 2 +- .../cmd_rule_engine_rule_disable.go | 2 +- .../enaptercli/cmd_rule_engine_rule_enable.go | 2 +- .../enaptercli/cmd_rule_engine_rule_get.go | 2 +- .../enaptercli/cmd_rule_engine_rule_list.go | 2 +- .../enaptercli/cmd_rule_engine_rule_logs.go | 2 +- .../enaptercli/cmd_rule_engine_rule_update.go | 2 +- .../cmd_rule_engine_rule_update_script.go | 2 +- .../app/enaptercli/cmd_rule_engine_suspend.go | 2 +- internal/app/enaptercli/cmd_site.go | 6 ++++-- internal/app/enaptercli/cmd_site_get.go | 2 +- internal/app/enaptercli/cmd_site_list.go | 2 +- internal/app/enaptercli/execute.go | 1 + .../app/enaptercli/testdata/helps/enapter | 6 ++++++ .../testdata/helps/enapter blueprint | 6 ++++++ .../testdata/helps/enapter blueprint profiles | 19 +++++++++++++++++++ .../enaptercli/testdata/helps/enapter device | 6 ++++++ .../testdata/helps/enapter device execution | 6 ++++++ .../testdata/helps/enapter provisioning | 6 ++++++ .../testdata/helps/enapter rule-engine | 6 ++++++ .../testdata/helps/enapter rule-engine rule | 6 ++++++ .../enaptercli/testdata/helps/enapter site | 6 ++++++ 48 files changed, 137 insertions(+), 47 deletions(-) create mode 100644 internal/app/enaptercli/testdata/helps/enapter blueprint profiles diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 9f9808f..bb9dea4 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -77,14 +77,20 @@ func (c *cmdBase) Before(cliCtx *cli.Context) error { return nil } -func (c *cmdBase) HelpTemplate() string { - return cli.CommandHelpTemplate + ` +const enapterAPIEnvVarsHelp = ` ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) ` + +func (c *cmdBase) CommandHelpTemplate() string { + return cli.CommandHelpTemplate + enapterAPIEnvVarsHelp +} + +func (c *cmdBase) SubcommandHelpTemplate() string { + return cli.SubcommandHelpTemplate + enapterAPIEnvVarsHelp } type doHTTPRequestParams struct { diff --git a/internal/app/enaptercli/cmd_blueprints.go b/internal/app/enaptercli/cmd_blueprints.go index 5dd161e..14bb51d 100644 --- a/internal/app/enaptercli/cmd_blueprints.go +++ b/internal/app/enaptercli/cmd_blueprints.go @@ -11,9 +11,11 @@ type cmdBlueprints struct { } func buildCmdBlueprints() *cli.Command { + cmd := &cmdBlueprints{} return &cli.Command{ - Name: "blueprint", - Usage: "Manage blueprints", + Name: "blueprint", + Usage: "Manage blueprints", + CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ buildCmdBlueprintsProfiles(), buildCmdBlueprintsUpload(), diff --git a/internal/app/enaptercli/cmd_blueprints_download.go b/internal/app/enaptercli/cmd_blueprints_download.go index 8310753..c92d71e 100644 --- a/internal/app/enaptercli/cmd_blueprints_download.go +++ b/internal/app/enaptercli/cmd_blueprints_download.go @@ -23,7 +23,7 @@ func buildCmdBlueprintsDownload() *cli.Command { return &cli.Command{ Name: "download", Usage: "Download blueprint zip from Platform", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_blueprints_get.go b/internal/app/enaptercli/cmd_blueprints_get.go index 23c0ba4..ddce15d 100644 --- a/internal/app/enaptercli/cmd_blueprints_get.go +++ b/internal/app/enaptercli/cmd_blueprints_get.go @@ -17,7 +17,7 @@ func buildCmdBlueprintsGet() *cli.Command { return &cli.Command{ Name: "get", Usage: "Get blueprint metadata", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_blueprints_profiles.go b/internal/app/enaptercli/cmd_blueprints_profiles.go index 73b47c8..1a72e03 100644 --- a/internal/app/enaptercli/cmd_blueprints_profiles.go +++ b/internal/app/enaptercli/cmd_blueprints_profiles.go @@ -9,9 +9,11 @@ type cmdBlueprintsProfiles struct { } func buildCmdBlueprintsProfiles() *cli.Command { + cmd := &cmdBlueprintsProfiles{} return &cli.Command{ - Name: "profiles", - Usage: "Manage blueprints profiles", + Name: "profiles", + Usage: "Manage blueprints profiles", + CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ buildCmdBlueprintsProfilesDownload(), buildCmdBlueprintsProfilesUpload(), diff --git a/internal/app/enaptercli/cmd_blueprints_profiles_download.go b/internal/app/enaptercli/cmd_blueprints_profiles_download.go index bfb8b83..66b2dea 100644 --- a/internal/app/enaptercli/cmd_blueprints_profiles_download.go +++ b/internal/app/enaptercli/cmd_blueprints_profiles_download.go @@ -20,7 +20,7 @@ func buildCmdBlueprintsProfilesDownload() *cli.Command { return &cli.Command{ Name: "download", Usage: "Download profiles zip from Platform", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_blueprints_profiles_upload.go b/internal/app/enaptercli/cmd_blueprints_profiles_upload.go index 2ac7ff0..fa40265 100644 --- a/internal/app/enaptercli/cmd_blueprints_profiles_upload.go +++ b/internal/app/enaptercli/cmd_blueprints_profiles_upload.go @@ -20,7 +20,7 @@ func buildCmdBlueprintsProfilesUpload() *cli.Command { return &cli.Command{ Name: "upload", Usage: "Upload profiles into Platform", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_blueprints_upload.go b/internal/app/enaptercli/cmd_blueprints_upload.go index 4785ce3..d2bc18e 100644 --- a/internal/app/enaptercli/cmd_blueprints_upload.go +++ b/internal/app/enaptercli/cmd_blueprints_upload.go @@ -21,7 +21,7 @@ func buildCmdBlueprintsUpload() *cli.Command { return &cli.Command{ Name: "upload", Usage: "Upload blueprint into Platform", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 89f9cc8..705470c 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -13,9 +13,11 @@ type cmdDevices struct { } func buildCmdDevices() *cli.Command { + cmd := &cmdDevices{} return &cli.Command{ - Name: "device", - Usage: "Manage devices", + Name: "device", + Usage: "Manage devices", + CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ buildCmdDevicesList(), buildCmdDevicesGet(), diff --git a/internal/app/enaptercli/cmd_device_assign_blueprint.go b/internal/app/enaptercli/cmd_device_assign_blueprint.go index dbd860b..740280f 100644 --- a/internal/app/enaptercli/cmd_device_assign_blueprint.go +++ b/internal/app/enaptercli/cmd_device_assign_blueprint.go @@ -22,7 +22,7 @@ func buildCmdDevicesAssignBlueprint() *cli.Command { return &cli.Command{ Name: "assign-blueprint", Usage: "Assign blueprint to device", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_device_delete.go b/internal/app/enaptercli/cmd_device_delete.go index 8338136..fc87028 100644 --- a/internal/app/enaptercli/cmd_device_delete.go +++ b/internal/app/enaptercli/cmd_device_delete.go @@ -17,7 +17,7 @@ func buildCmdDevicesDelete() *cli.Command { return &cli.Command{ Name: "delete", Usage: "Delete a device", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_device_execute_command.go b/internal/app/enaptercli/cmd_device_execute_command.go index b7d7069..a53206d 100644 --- a/internal/app/enaptercli/cmd_device_execute_command.go +++ b/internal/app/enaptercli/cmd_device_execute_command.go @@ -23,7 +23,7 @@ func buildCmdDevicesExecuteCommand() *cli.Command { return &cli.Command{ Name: "execute-command", Usage: "Execute a device command", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_device_execution.go b/internal/app/enaptercli/cmd_device_execution.go index f8d1652..7528874 100644 --- a/internal/app/enaptercli/cmd_device_execution.go +++ b/internal/app/enaptercli/cmd_device_execution.go @@ -14,9 +14,11 @@ type cmdDeviceExecution struct { } func buildCmdDeviceExecution() *cli.Command { + cmd := &cmdDeviceExecution{} return &cli.Command{ - Name: "execution", - Usage: "Manage device command executions", + Name: "execution", + Usage: "Manage device command executions", + CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ buildCmdDeviceExecutionList(), buildCmdDeviceExecutionGet(), diff --git a/internal/app/enaptercli/cmd_device_execution_get.go b/internal/app/enaptercli/cmd_device_execution_get.go index e597ce4..e691a30 100644 --- a/internal/app/enaptercli/cmd_device_execution_get.go +++ b/internal/app/enaptercli/cmd_device_execution_get.go @@ -20,7 +20,7 @@ func buildCmdDeviceExecutionGet() *cli.Command { return &cli.Command{ Name: "get", Usage: "Get a device command execution", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_device_execution_list.go b/internal/app/enaptercli/cmd_device_execution_list.go index be2203f..c82680d 100644 --- a/internal/app/enaptercli/cmd_device_execution_list.go +++ b/internal/app/enaptercli/cmd_device_execution_list.go @@ -19,7 +19,7 @@ func buildCmdDeviceExecutionList() *cli.Command { return &cli.Command{ Name: "list", Usage: "List device command executions", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_device_get.go b/internal/app/enaptercli/cmd_device_get.go index 94b5917..94495e8 100644 --- a/internal/app/enaptercli/cmd_device_get.go +++ b/internal/app/enaptercli/cmd_device_get.go @@ -20,7 +20,7 @@ func buildCmdDevicesGet() *cli.Command { return &cli.Command{ Name: "get", Usage: "Get a device info", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_device_list.go b/internal/app/enaptercli/cmd_device_list.go index 277872b..f06f0b1 100644 --- a/internal/app/enaptercli/cmd_device_list.go +++ b/internal/app/enaptercli/cmd_device_list.go @@ -20,7 +20,7 @@ func buildCmdDevicesList() *cli.Command { return &cli.Command{ Name: "list", Usage: "List user devices", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_device_logs.go b/internal/app/enaptercli/cmd_device_logs.go index b24b63a..ad615c7 100644 --- a/internal/app/enaptercli/cmd_device_logs.go +++ b/internal/app/enaptercli/cmd_device_logs.go @@ -31,7 +31,7 @@ func buildCmdDevicesLogs() *cli.Command { return &cli.Command{ Name: "logs", Usage: "Show device logs", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_provisioning.go b/internal/app/enaptercli/cmd_provisioning.go index cf019b6..d5a7ea6 100644 --- a/internal/app/enaptercli/cmd_provisioning.go +++ b/internal/app/enaptercli/cmd_provisioning.go @@ -9,9 +9,11 @@ type cmdProvisioning struct { } func buildCmdProvisioning() *cli.Command { + cmd := &cmdProvisioning{} return &cli.Command{ - Name: "provisioning", - Usage: "Create devices of different types", + Name: "provisioning", + Usage: "Create devices of different types", + CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ buildCmdProvisioningStandalone(), buildCmdProvisioningLua(), diff --git a/internal/app/enaptercli/cmd_provisioning_lua_device.go b/internal/app/enaptercli/cmd_provisioning_lua_device.go index 00b43a2..06ddde0 100644 --- a/internal/app/enaptercli/cmd_provisioning_lua_device.go +++ b/internal/app/enaptercli/cmd_provisioning_lua_device.go @@ -24,7 +24,7 @@ func buildCmdProvisioningLua() *cli.Command { return &cli.Command{ Name: "lua-device", Usage: "Create a new Lua device", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_provisioning_standalone.go b/internal/app/enaptercli/cmd_provisioning_standalone.go index 7854023..ae1880b 100644 --- a/internal/app/enaptercli/cmd_provisioning_standalone.go +++ b/internal/app/enaptercli/cmd_provisioning_standalone.go @@ -21,7 +21,7 @@ func buildCmdProvisioningStandalone() *cli.Command { return &cli.Command{ Name: "standalone", Usage: "Create a new standalone device", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_rule_engine.go b/internal/app/enaptercli/cmd_rule_engine.go index 9152991..8632686 100644 --- a/internal/app/enaptercli/cmd_rule_engine.go +++ b/internal/app/enaptercli/cmd_rule_engine.go @@ -13,9 +13,11 @@ type cmdRuleEngine struct { } func buildCmdRuleEngine() *cli.Command { + cmd := &cmdRuleEngine{} return &cli.Command{ - Name: "rule-engine", - Usage: "Manage the rule engine", + Name: "rule-engine", + Usage: "Manage the rule engine", + CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ buildCmdRuleEngineGet(), buildCmdRuleEngineSuspend(), diff --git a/internal/app/enaptercli/cmd_rule_engine_get.go b/internal/app/enaptercli/cmd_rule_engine_get.go index 7ff0479..88c5c4d 100644 --- a/internal/app/enaptercli/cmd_rule_engine_get.go +++ b/internal/app/enaptercli/cmd_rule_engine_get.go @@ -16,7 +16,7 @@ func buildCmdRuleEngineGet() *cli.Command { return &cli.Command{ Name: "get", Usage: "Get the rule engine", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_rule_engine_resume.go b/internal/app/enaptercli/cmd_rule_engine_resume.go index c1a5b9f..b40b0eb 100644 --- a/internal/app/enaptercli/cmd_rule_engine_resume.go +++ b/internal/app/enaptercli/cmd_rule_engine_resume.go @@ -16,7 +16,7 @@ func buildCmdRuleEngineResume() *cli.Command { return &cli.Command{ Name: "resume", Usage: "Resume execution of rules", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_rule_engine_rule.go b/internal/app/enaptercli/cmd_rule_engine_rule.go index 2c209cf..7fdfbf0 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule.go @@ -18,9 +18,11 @@ type cmdRuleEngineRule struct { } func buildCmdRuleEngineRule() *cli.Command { + cmd := &cmdRuleEngineRule{} return &cli.Command{ - Name: "rule", - Usage: "Manage rules", + Name: "rule", + Usage: "Manage rules", + CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ buildCmdRuleEngineRuleCreate(), buildCmdRuleEngineRuleDelete(), diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_create.go b/internal/app/enaptercli/cmd_rule_engine_rule_create.go index 258b757..f7a6af3 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_create.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_create.go @@ -27,7 +27,7 @@ func buildCmdRuleEngineRuleCreate() *cli.Command { return &cli.Command{ Name: "create", Usage: "Create a new rule", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_delete.go b/internal/app/enaptercli/cmd_rule_engine_rule_delete.go index 72b84e3..9e92c54 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_delete.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_delete.go @@ -20,7 +20,7 @@ func buildCmdRuleEngineRuleDelete() *cli.Command { return &cli.Command{ Name: "delete", Usage: "Delete one or more rules", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_disable.go b/internal/app/enaptercli/cmd_rule_engine_rule_disable.go index f4ff79f..a5c76fc 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_disable.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_disable.go @@ -20,7 +20,7 @@ func buildCmdRuleEngineRuleDisable() *cli.Command { return &cli.Command{ Name: "disable", Usage: "Disable one or more rules", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_enable.go b/internal/app/enaptercli/cmd_rule_engine_rule_enable.go index 6338f19..44e9580 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_enable.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_enable.go @@ -20,7 +20,7 @@ func buildCmdRuleEngineRuleEnable() *cli.Command { return &cli.Command{ Name: "enable", Usage: "Enable one or more rules", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_get.go b/internal/app/enaptercli/cmd_rule_engine_rule_get.go index a637694..574600d 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_get.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_get.go @@ -17,7 +17,7 @@ func buildCmdRuleEngineRuleGet() *cli.Command { return &cli.Command{ Name: "get", Usage: "Get a rule", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_list.go b/internal/app/enaptercli/cmd_rule_engine_rule_list.go index 90aa6a8..cb1de8e 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_list.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_list.go @@ -16,7 +16,7 @@ func buildCmdRuleEngineRuleList() *cli.Command { return &cli.Command{ Name: "list", Usage: "List rules", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_logs.go b/internal/app/enaptercli/cmd_rule_engine_rule_logs.go index 2add98b..53609cf 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_logs.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_logs.go @@ -20,7 +20,7 @@ func buildCmdRuleEngineRuleLogs() *cli.Command { return &cli.Command{ Name: "logs", Usage: "Show rule logs", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update.go b/internal/app/enaptercli/cmd_rule_engine_rule_update.go index 405b114..9009ec6 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_update.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update.go @@ -20,7 +20,7 @@ func buildCmdRuleEngineRuleUpdate() *cli.Command { return &cli.Command{ Name: "update", Usage: "Update a rule", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go index fa46f98..3684f64 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go @@ -26,7 +26,7 @@ func buildCmdRuleEngineRuleUpdateScript() *cli.Command { return &cli.Command{ Name: "update-script", Usage: "Update the script of a rule", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_rule_engine_suspend.go b/internal/app/enaptercli/cmd_rule_engine_suspend.go index fa0f5cb..5637e48 100644 --- a/internal/app/enaptercli/cmd_rule_engine_suspend.go +++ b/internal/app/enaptercli/cmd_rule_engine_suspend.go @@ -16,7 +16,7 @@ func buildCmdRuleEngineSuspend() *cli.Command { return &cli.Command{ Name: "suspend", Usage: "Suspend execution of rules", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_site.go b/internal/app/enaptercli/cmd_site.go index 2755ebf..6ed356d 100644 --- a/internal/app/enaptercli/cmd_site.go +++ b/internal/app/enaptercli/cmd_site.go @@ -13,9 +13,11 @@ type cmdSite struct { } func buildCmdSites() *cli.Command { + cmd := &cmdSite{} return &cli.Command{ - Name: "site", - Usage: "Manage sites", + Name: "site", + Usage: "Manage sites", + CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ buildCmdSitesList(), buildCmdSiteGet(), diff --git a/internal/app/enaptercli/cmd_site_get.go b/internal/app/enaptercli/cmd_site_get.go index d32b7a1..541a6fe 100644 --- a/internal/app/enaptercli/cmd_site_get.go +++ b/internal/app/enaptercli/cmd_site_get.go @@ -17,7 +17,7 @@ func buildCmdSiteGet() *cli.Command { return &cli.Command{ Name: "get", Usage: "Get a site", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/cmd_site_list.go b/internal/app/enaptercli/cmd_site_list.go index 9400566..feb785e 100644 --- a/internal/app/enaptercli/cmd_site_list.go +++ b/internal/app/enaptercli/cmd_site_list.go @@ -20,7 +20,7 @@ func buildCmdSitesList() *cli.Command { return &cli.Command{ Name: "list", Usage: "List user sites", - CustomHelpTemplate: cmd.HelpTemplate(), + CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, Action: func(cliCtx *cli.Context) error { diff --git a/internal/app/enaptercli/execute.go b/internal/app/enaptercli/execute.go index 9785c34..4c5a9ad 100644 --- a/internal/app/enaptercli/execute.go +++ b/internal/app/enaptercli/execute.go @@ -19,6 +19,7 @@ func NewApp() *cli.App { app.Usage = "Command line interface for Enapter services." app.Description = "Enapter CLI requires access token for authentication. " + "The token can be obtained in your Enapter Cloud account settings." + app.CustomAppHelpTemplate = cli.AppHelpTemplate + enapterAPIEnvVarsHelp app.Commands = []*cli.Command{ buildCmdSites(), diff --git a/internal/app/enaptercli/testdata/helps/enapter b/internal/app/enaptercli/testdata/helps/enapter index 05404c9..43e4ef5 100644 --- a/internal/app/enaptercli/testdata/helps/enapter +++ b/internal/app/enaptercli/testdata/helps/enapter @@ -17,3 +17,9 @@ COMMANDS: GLOBAL OPTIONS: --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint b/internal/app/enaptercli/testdata/helps/enapter blueprint index 01e01dc..7c70f40 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint @@ -13,3 +13,9 @@ COMMANDS: OPTIONS: --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles new file mode 100644 index 0000000..48597a6 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles @@ -0,0 +1,19 @@ +NAME: + enaptercli.test blueprint profiles - Manage blueprints profiles + +USAGE: + enaptercli.test blueprint profiles command [command options] + +COMMANDS: + download Download profiles zip from Platform + upload Upload profiles into Platform + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index 08f896c..b8f24e9 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -16,3 +16,9 @@ COMMANDS: OPTIONS: --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution b/internal/app/enaptercli/testdata/helps/enapter device execution index e7ff6bd..b3a4d56 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execution +++ b/internal/app/enaptercli/testdata/helps/enapter device execution @@ -11,3 +11,9 @@ COMMANDS: OPTIONS: --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning b/internal/app/enaptercli/testdata/helps/enapter provisioning index 5e26987..60cd959 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning @@ -11,3 +11,9 @@ COMMANDS: OPTIONS: --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine b/internal/app/enaptercli/testdata/helps/enapter rule-engine index 27613bb..0e09d94 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine @@ -13,3 +13,9 @@ COMMANDS: OPTIONS: --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule index c75e7b6..1db01d3 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule @@ -18,3 +18,9 @@ COMMANDS: OPTIONS: --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + diff --git a/internal/app/enaptercli/testdata/helps/enapter site b/internal/app/enaptercli/testdata/helps/enapter site index 59f088a..3f47ff6 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site +++ b/internal/app/enaptercli/testdata/helps/enapter site @@ -11,3 +11,9 @@ COMMANDS: OPTIONS: --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + From fd8aef8738d56e04b1501e36612bbd011ef9f5de Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 27 Jun 2025 18:01:46 +0300 Subject: [PATCH 42/88] feat: rework device command execution --- internal/app/enaptercli/cmd_device.go | 3 +-- ...ice_execution.go => cmd_device_command.go} | 19 ++++++++++--------- ...mmand.go => cmd_device_command_execute.go} | 12 ++++++------ ...ution_get.go => cmd_device_command_get.go} | 16 ++++++++-------- ...ion_list.go => cmd_device_command_list.go} | 16 ++++++++-------- .../enaptercli/testdata/helps/enapter device | 3 +-- ...evice execution => enapter device command} | 5 +++-- ...command => enapter device command execute} | 4 ++-- ...ecution get => enapter device command get} | 4 ++-- ...ution list => enapter device command list} | 4 ++-- 10 files changed, 43 insertions(+), 43 deletions(-) rename internal/app/enaptercli/{cmd_device_execution.go => cmd_device_command.go} (60%) rename internal/app/enaptercli/{cmd_device_execute_command.go => cmd_device_command_execute.go} (84%) rename internal/app/enaptercli/{cmd_device_execution_get.go => cmd_device_command_get.go} (76%) rename internal/app/enaptercli/{cmd_device_execution_list.go => cmd_device_command_list.go} (72%) rename internal/app/enaptercli/testdata/helps/{enapter device execution => enapter device command} (74%) rename internal/app/enaptercli/testdata/helps/{enapter device execute-command => enapter device command execute} (86%) rename internal/app/enaptercli/testdata/helps/{enapter device execution get => enapter device command get} (86%) rename internal/app/enaptercli/testdata/helps/{enapter device execution list => enapter device command list} (85%) diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 705470c..90fb7b1 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -24,8 +24,7 @@ func buildCmdDevices() *cli.Command { buildCmdDevicesAssignBlueprint(), buildCmdDevicesLogs(), buildCmdDevicesDelete(), - buildCmdDevicesExecuteCommand(), - buildCmdDeviceExecution(), + buildCmdDeviceCommand(), }, } } diff --git a/internal/app/enaptercli/cmd_device_execution.go b/internal/app/enaptercli/cmd_device_command.go similarity index 60% rename from internal/app/enaptercli/cmd_device_execution.go rename to internal/app/enaptercli/cmd_device_command.go index 7528874..ef200bf 100644 --- a/internal/app/enaptercli/cmd_device_execution.go +++ b/internal/app/enaptercli/cmd_device_command.go @@ -8,25 +8,26 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDeviceExecution struct { +type cmdDeviceCommand struct { cmdDevices deviceID string } -func buildCmdDeviceExecution() *cli.Command { - cmd := &cmdDeviceExecution{} +func buildCmdDeviceCommand() *cli.Command { + cmd := &cmdDeviceCommand{} return &cli.Command{ - Name: "execution", - Usage: "Manage device command executions", + Name: "command", + Usage: "Manage device commands", CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ - buildCmdDeviceExecutionList(), - buildCmdDeviceExecutionGet(), + buildCmdDeviceCommandExecute(), + buildCmdDeviceCommandList(), + buildCmdDeviceCommandGet(), }, } } -func (c *cmdDeviceExecution) Flags() []cli.Flag { +func (c *cmdDeviceCommand) Flags() []cli.Flag { flags := c.cmdDevices.Flags() return append(flags, &cli.StringFlag{ @@ -39,7 +40,7 @@ func (c *cmdDeviceExecution) Flags() []cli.Flag { ) } -func (c *cmdDeviceExecution) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { +func (c *cmdDeviceCommand) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { path, err := url.JoinPath(c.deviceID, "command_executions", p.Path) if err != nil { return fmt.Errorf("join path: %w", err) diff --git a/internal/app/enaptercli/cmd_device_execute_command.go b/internal/app/enaptercli/cmd_device_command_execute.go similarity index 84% rename from internal/app/enaptercli/cmd_device_execute_command.go rename to internal/app/enaptercli/cmd_device_command_execute.go index a53206d..b968106 100644 --- a/internal/app/enaptercli/cmd_device_execute_command.go +++ b/internal/app/enaptercli/cmd_device_command_execute.go @@ -10,7 +10,7 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDevicesExecuteCommand struct { +type cmdDeviceCommandExecute struct { cmdDevices deviceID string cmdName string @@ -18,10 +18,10 @@ type cmdDevicesExecuteCommand struct { ephemeral bool } -func buildCmdDevicesExecuteCommand() *cli.Command { - cmd := &cmdDevicesExecuteCommand{} +func buildCmdDeviceCommandExecute() *cli.Command { + cmd := &cmdDeviceCommandExecute{} return &cli.Command{ - Name: "execute-command", + Name: "execute", Usage: "Execute a device command", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), @@ -32,7 +32,7 @@ func buildCmdDevicesExecuteCommand() *cli.Command { } } -func (c *cmdDevicesExecuteCommand) Flags() []cli.Flag { +func (c *cmdDeviceCommandExecute) Flags() []cli.Flag { flags := c.cmdDevices.Flags() return append(flags, &cli.StringFlag{ @@ -60,7 +60,7 @@ func (c *cmdDevicesExecuteCommand) Flags() []cli.Flag { ) } -func (c *cmdDevicesExecuteCommand) do(ctx context.Context) error { +func (c *cmdDeviceCommandExecute) do(ctx context.Context) error { reqBody := struct { Name string `json:"name"` Args json.RawMessage `json:"arguments,omitempty"` diff --git a/internal/app/enaptercli/cmd_device_execution_get.go b/internal/app/enaptercli/cmd_device_command_get.go similarity index 76% rename from internal/app/enaptercli/cmd_device_execution_get.go rename to internal/app/enaptercli/cmd_device_command_get.go index e691a30..fbe54ce 100644 --- a/internal/app/enaptercli/cmd_device_execution_get.go +++ b/internal/app/enaptercli/cmd_device_command_get.go @@ -9,14 +9,14 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDeviceExecutionGet struct { - cmdDeviceExecution +type cmdDeviceCommandGet struct { + cmdDeviceCommand executionID string expand []string } -func buildCmdDeviceExecutionGet() *cli.Command { - cmd := &cmdDeviceExecutionGet{} +func buildCmdDeviceCommandGet() *cli.Command { + cmd := &cmdDeviceCommandGet{} return &cli.Command{ Name: "get", Usage: "Get a device command execution", @@ -29,8 +29,8 @@ func buildCmdDeviceExecutionGet() *cli.Command { } } -func (c *cmdDeviceExecutionGet) Flags() []cli.Flag { - flags := c.cmdDeviceExecution.Flags() +func (c *cmdDeviceCommandGet) Flags() []cli.Flag { + flags := c.cmdDeviceCommand.Flags() return append(flags, &cli.StringFlag{ Name: "execution-id", @@ -47,14 +47,14 @@ func (c *cmdDeviceExecutionGet) Flags() []cli.Flag { ) } -func (c *cmdDeviceExecutionGet) Before(cliCtx *cli.Context) error { +func (c *cmdDeviceCommandGet) Before(cliCtx *cli.Context) error { if err := c.cmdDevices.Before(cliCtx); err != nil { return err } return validateExpandFlag(cliCtx, []string{"log"}) } -func (c *cmdDeviceExecutionGet) do(ctx context.Context) error { +func (c *cmdDeviceCommandGet) do(ctx context.Context) error { query := url.Values{} if len(c.expand) != 0 { query.Set("expand", strings.Join(c.expand, ",")) diff --git a/internal/app/enaptercli/cmd_device_execution_list.go b/internal/app/enaptercli/cmd_device_command_list.go similarity index 72% rename from internal/app/enaptercli/cmd_device_execution_list.go rename to internal/app/enaptercli/cmd_device_command_list.go index c82680d..bb16f39 100644 --- a/internal/app/enaptercli/cmd_device_execution_list.go +++ b/internal/app/enaptercli/cmd_device_command_list.go @@ -9,13 +9,13 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDeviceExecutionList struct { - cmdDeviceExecution +type cmdDeviceCommandList struct { + cmdDeviceCommand expand []string } -func buildCmdDeviceExecutionList() *cli.Command { - cmd := &cmdDeviceExecutionList{} +func buildCmdDeviceCommandList() *cli.Command { + cmd := &cmdDeviceCommandList{} return &cli.Command{ Name: "list", Usage: "List device command executions", @@ -28,8 +28,8 @@ func buildCmdDeviceExecutionList() *cli.Command { } } -func (c *cmdDeviceExecutionList) Flags() []cli.Flag { - flags := c.cmdDeviceExecution.Flags() +func (c *cmdDeviceCommandList) Flags() []cli.Flag { + flags := c.cmdDeviceCommand.Flags() return append(flags, &cli.MultiStringFlag{ Target: &cli.StringSliceFlag{ @@ -41,14 +41,14 @@ func (c *cmdDeviceExecutionList) Flags() []cli.Flag { ) } -func (c *cmdDeviceExecutionList) Before(cliCtx *cli.Context) error { +func (c *cmdDeviceCommandList) Before(cliCtx *cli.Context) error { if err := c.cmdDevices.Before(cliCtx); err != nil { return err } return validateExpandFlag(cliCtx, []string{"ephemeral"}) } -func (c *cmdDeviceExecutionList) do(ctx context.Context) error { +func (c *cmdDeviceCommandList) do(ctx context.Context) error { query := url.Values{} if len(c.expand) != 0 { query.Set("expand", strings.Join(c.expand, ",")) diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index b8f24e9..fc16426 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -10,8 +10,7 @@ COMMANDS: assign-blueprint Assign blueprint to device logs Show device logs delete Delete a device - execute-command Execute a device command - execution Manage device command executions + command Manage device commands help, h Shows a list of commands or help for one command OPTIONS: diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution b/internal/app/enaptercli/testdata/helps/enapter device command similarity index 74% rename from internal/app/enaptercli/testdata/helps/enapter device execution rename to internal/app/enaptercli/testdata/helps/enapter device command index b3a4d56..46a20bc 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execution +++ b/internal/app/enaptercli/testdata/helps/enapter device command @@ -1,10 +1,11 @@ NAME: - enaptercli.test device execution - Manage device command executions + enaptercli.test device command - Manage device commands USAGE: - enaptercli.test device execution command [command options] + enaptercli.test device command command [command options] COMMANDS: + execute Execute a device command list List device command executions get Get a device command execution help, h Shows a list of commands or help for one command diff --git a/internal/app/enaptercli/testdata/helps/enapter device execute-command b/internal/app/enaptercli/testdata/helps/enapter device command execute similarity index 86% rename from internal/app/enaptercli/testdata/helps/enapter device execute-command rename to internal/app/enaptercli/testdata/helps/enapter device command execute index 4181a1e..ae7d6d2 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execute-command +++ b/internal/app/enaptercli/testdata/helps/enapter device command execute @@ -1,8 +1,8 @@ NAME: - enaptercli.test device execute-command - Execute a device command + enaptercli.test device command execute - Execute a device command USAGE: - enaptercli.test device execute-command [command options] + enaptercli.test device command execute [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution get b/internal/app/enaptercli/testdata/helps/enapter device command get similarity index 86% rename from internal/app/enaptercli/testdata/helps/enapter device execution get rename to internal/app/enaptercli/testdata/helps/enapter device command get index 610b674..8299cbd 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execution get +++ b/internal/app/enaptercli/testdata/helps/enapter device command get @@ -1,8 +1,8 @@ NAME: - enaptercli.test device execution get - Get a device command execution + enaptercli.test device command get - Get a device command execution USAGE: - enaptercli.test device execution get [command options] + enaptercli.test device command get [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] diff --git a/internal/app/enaptercli/testdata/helps/enapter device execution list b/internal/app/enaptercli/testdata/helps/enapter device command list similarity index 85% rename from internal/app/enaptercli/testdata/helps/enapter device execution list rename to internal/app/enaptercli/testdata/helps/enapter device command list index 25d7cfb..3365208 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device execution list +++ b/internal/app/enaptercli/testdata/helps/enapter device command list @@ -1,8 +1,8 @@ NAME: - enaptercli.test device execution list - List device command executions + enaptercli.test device command list - List device command executions USAGE: - enaptercli.test device execution list [command options] + enaptercli.test device command list [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] From ad86590eca793ff12396b21c60ca16c53fd8ef85 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 27 Jun 2025 18:05:53 +0300 Subject: [PATCH 43/88] feat: cleanup unnecessary option prefixes --- .../app/enaptercli/cmd_blueprints_profiles_upload.go | 2 +- internal/app/enaptercli/cmd_device_command_execute.go | 6 +++--- .../testdata/helps/enapter blueprint profiles upload | 10 +++++----- .../testdata/helps/enapter device command execute | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/app/enaptercli/cmd_blueprints_profiles_upload.go b/internal/app/enaptercli/cmd_blueprints_profiles_upload.go index fa40265..70a277f 100644 --- a/internal/app/enaptercli/cmd_blueprints_profiles_upload.go +++ b/internal/app/enaptercli/cmd_blueprints_profiles_upload.go @@ -32,7 +32,7 @@ func buildCmdBlueprintsProfilesUpload() *cli.Command { func (c *cmdBlueprintsProfilesUpload) Flags() []cli.Flag { flags := c.cmdBlueprintsProfiles.Flags() return append(flags, &cli.StringFlag{ - Name: "profiles-path", + Name: "path", Aliases: []string{"p"}, Usage: "profiles zip file path", Destination: &c.profilesPath, diff --git a/internal/app/enaptercli/cmd_device_command_execute.go b/internal/app/enaptercli/cmd_device_command_execute.go index b968106..43e99a6 100644 --- a/internal/app/enaptercli/cmd_device_command_execute.go +++ b/internal/app/enaptercli/cmd_device_command_execute.go @@ -42,14 +42,14 @@ func (c *cmdDeviceCommandExecute) Flags() []cli.Flag { Required: true, }, &cli.StringFlag{ - Name: "cmd-name", + Name: "name", Usage: "command name", Destination: &c.cmdName, Required: true, }, &cli.StringFlag{ - Name: "cmd-args", - Usage: "command args (should be a JSON string)", + Name: "arguments", + Usage: "command arguments (should be a JSON string)", Destination: &c.cmdArgs, }, &cli.BoolFlag{ diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload index 7e30805..2c9b1d0 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload @@ -5,11 +5,11 @@ USAGE: enaptercli.test blueprint profiles upload [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) - --profiles-path value, -p value profiles zip file path - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --path value, -p value profiles zip file path + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter device command execute b/internal/app/enaptercli/testdata/helps/enapter device command execute index ae7d6d2..b4cd45a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command execute +++ b/internal/app/enaptercli/testdata/helps/enapter device command execute @@ -9,8 +9,8 @@ OPTIONS: --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --device-id value device ID - --cmd-name value command name - --cmd-args value command args (should be a JSON string) + --name value command name + --arguments value command arguments (should be a JSON string) --ephemeral run command in ephemeral mode (default: false) --help, -h show help From b3409277abdd955f6b6879c04d40ff24d6bcdc47 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 27 Jun 2025 18:10:15 +0300 Subject: [PATCH 44/88] fix: site-id is not required to provision standalone on Gateway --- internal/app/enaptercli/cmd_provisioning_standalone.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/app/enaptercli/cmd_provisioning_standalone.go b/internal/app/enaptercli/cmd_provisioning_standalone.go index ae1880b..0ef7f13 100644 --- a/internal/app/enaptercli/cmd_provisioning_standalone.go +++ b/internal/app/enaptercli/cmd_provisioning_standalone.go @@ -37,7 +37,6 @@ func (c *cmdProvisioningStandalone) Flags() []cli.Flag { Aliases: []string{"s"}, Usage: "site ID where to craate device", Destination: &c.siteID, - Required: true, }, &cli.StringFlag{ Name: "device-name", Aliases: []string{"n"}, @@ -48,7 +47,7 @@ func (c *cmdProvisioningStandalone) Flags() []cli.Flag { } func (c *cmdProvisioningStandalone) do(ctx context.Context) error { - body, err := json.Marshal(map[string]interface{}{ + body, err := json.Marshal(map[string]any{ "site_id": c.siteID, "name": c.deviceName, }) From b4373f672f050e5481dc54780801c765d3e3cdd0 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 27 Jun 2025 18:17:53 +0300 Subject: [PATCH 45/88] feat: add missed short name for device-id option --- .../app/enaptercli/cmd_device_command_execute.go | 1 + internal/app/enaptercli/cmd_device_delete.go | 1 + .../helps/enapter device command execute | 16 ++++++++-------- .../testdata/helps/enapter device delete | 10 +++++----- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/internal/app/enaptercli/cmd_device_command_execute.go b/internal/app/enaptercli/cmd_device_command_execute.go index 43e99a6..43d8d49 100644 --- a/internal/app/enaptercli/cmd_device_command_execute.go +++ b/internal/app/enaptercli/cmd_device_command_execute.go @@ -37,6 +37,7 @@ func (c *cmdDeviceCommandExecute) Flags() []cli.Flag { return append(flags, &cli.StringFlag{ Name: "device-id", + Aliases: []string{"d"}, Usage: "device ID", Destination: &c.deviceID, Required: true, diff --git a/internal/app/enaptercli/cmd_device_delete.go b/internal/app/enaptercli/cmd_device_delete.go index fc87028..b655756 100644 --- a/internal/app/enaptercli/cmd_device_delete.go +++ b/internal/app/enaptercli/cmd_device_delete.go @@ -31,6 +31,7 @@ func (c *cmdDevicesDelete) Flags() []cli.Flag { return append(flags, &cli.StringFlag{ Name: "device-id", + Aliases: []string{"d"}, Usage: "device ID", Destination: &c.deviceID, Required: true, diff --git a/internal/app/enaptercli/testdata/helps/enapter device command execute b/internal/app/enaptercli/testdata/helps/enapter device command execute index b4cd45a..bbf0b2a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command execute +++ b/internal/app/enaptercli/testdata/helps/enapter device command execute @@ -5,14 +5,14 @@ USAGE: enaptercli.test device command execute [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) - --device-id value device ID - --name value command name - --arguments value command arguments (should be a JSON string) - --ephemeral run command in ephemeral mode (default: false) - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --device-id value, -d value device ID + --name value command name + --arguments value command arguments (should be a JSON string) + --ephemeral run command in ephemeral mode (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter device delete b/internal/app/enaptercli/testdata/helps/enapter device delete index a9c1e8a..c4e1dbb 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device delete +++ b/internal/app/enaptercli/testdata/helps/enapter device delete @@ -5,11 +5,11 @@ USAGE: enaptercli.test device delete [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) - --device-id value device ID - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --device-id value, -d value device ID + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token From c10afcd209437c921fcb51d01e16fb9d2ab7d08e Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 30 Jun 2025 12:52:42 +0300 Subject: [PATCH 46/88] feat: hide ephemeral API --- .../enaptercli/cmd_device_command_execute.go | 1 + .../app/enaptercli/cmd_device_command_list.go | 28 ------------------- .../helps/enapter device command execute | 1 - .../helps/enapter device command list | 11 ++++---- 4 files changed, 6 insertions(+), 35 deletions(-) diff --git a/internal/app/enaptercli/cmd_device_command_execute.go b/internal/app/enaptercli/cmd_device_command_execute.go index 43d8d49..919c1b5 100644 --- a/internal/app/enaptercli/cmd_device_command_execute.go +++ b/internal/app/enaptercli/cmd_device_command_execute.go @@ -57,6 +57,7 @@ func (c *cmdDeviceCommandExecute) Flags() []cli.Flag { Name: "ephemeral", Usage: "run command in ephemeral mode", Destination: &c.ephemeral, + Hidden: true, }, ) } diff --git a/internal/app/enaptercli/cmd_device_command_list.go b/internal/app/enaptercli/cmd_device_command_list.go index bb16f39..3cc6ee4 100644 --- a/internal/app/enaptercli/cmd_device_command_list.go +++ b/internal/app/enaptercli/cmd_device_command_list.go @@ -3,15 +3,12 @@ package enaptercli import ( "context" "net/http" - "net/url" - "strings" "github.com/urfave/cli/v2" ) type cmdDeviceCommandList struct { cmdDeviceCommand - expand []string } func buildCmdDeviceCommandList() *cli.Command { @@ -28,33 +25,8 @@ func buildCmdDeviceCommandList() *cli.Command { } } -func (c *cmdDeviceCommandList) Flags() []cli.Flag { - flags := c.cmdDeviceCommand.Flags() - return append(flags, - &cli.MultiStringFlag{ - Target: &cli.StringSliceFlag{ - Name: "expand", - Usage: "coma separated list of expanded options", - }, - Destination: &c.expand, - }, - ) -} - -func (c *cmdDeviceCommandList) Before(cliCtx *cli.Context) error { - if err := c.cmdDevices.Before(cliCtx); err != nil { - return err - } - return validateExpandFlag(cliCtx, []string{"ephemeral"}) -} - func (c *cmdDeviceCommandList) do(ctx context.Context) error { - query := url.Values{} - if len(c.expand) != 0 { - query.Set("expand", strings.Join(c.expand, ",")) - } return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, - Query: query, }) } diff --git a/internal/app/enaptercli/testdata/helps/enapter device command execute b/internal/app/enaptercli/testdata/helps/enapter device command execute index bbf0b2a..0dd6c3b 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command execute +++ b/internal/app/enaptercli/testdata/helps/enapter device command execute @@ -11,7 +11,6 @@ OPTIONS: --device-id value, -d value device ID --name value command name --arguments value command arguments (should be a JSON string) - --ephemeral run command in ephemeral mode (default: false) --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter device command list b/internal/app/enaptercli/testdata/helps/enapter device command list index 3365208..269e351 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command list +++ b/internal/app/enaptercli/testdata/helps/enapter device command list @@ -5,12 +5,11 @@ USAGE: enaptercli.test device command list [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) - --device-id value, -d value device ID - --expand value [ --expand value ] coma separated list of expanded options - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --device-id value, -d value device ID + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token From c28a28b0472b9472ba9ddd6aeed94769c5167534 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 30 Jun 2025 13:13:50 +0300 Subject: [PATCH 47/88] docs: add expand supported values to help messages --- internal/app/enaptercli/cmd_device_command_get.go | 2 +- internal/app/enaptercli/cmd_device_get.go | 5 +++-- internal/app/enaptercli/cmd_device_list.go | 5 +++-- .../app/enaptercli/testdata/helps/enapter device command get | 2 +- internal/app/enaptercli/testdata/helps/enapter device get | 2 +- internal/app/enaptercli/testdata/helps/enapter device list | 2 +- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/internal/app/enaptercli/cmd_device_command_get.go b/internal/app/enaptercli/cmd_device_command_get.go index fbe54ce..a929d42 100644 --- a/internal/app/enaptercli/cmd_device_command_get.go +++ b/internal/app/enaptercli/cmd_device_command_get.go @@ -40,7 +40,7 @@ func (c *cmdDeviceCommandGet) Flags() []cli.Flag { }, &cli.MultiStringFlag{ Target: &cli.StringSliceFlag{ Name: "expand", - Usage: "coma separated list of expanded options", + Usage: "coma separated list of expanded options (supported values: log)", }, Destination: &c.expand, }, diff --git a/internal/app/enaptercli/cmd_device_get.go b/internal/app/enaptercli/cmd_device_get.go index 94495e8..f4aea47 100644 --- a/internal/app/enaptercli/cmd_device_get.go +++ b/internal/app/enaptercli/cmd_device_get.go @@ -39,8 +39,9 @@ func (c *cmdDevicesGet) Flags() []cli.Flag { Required: true, }, &cli.MultiStringFlag{ Target: &cli.StringSliceFlag{ - Name: "expand", - Usage: "coma separated list of expanded device info", + Name: "expand", + Usage: "coma separated list of expanded device info (supported values: " + + "connectivity, manifest, properties, communication_info, site)", }, Destination: &c.expand, }) diff --git a/internal/app/enaptercli/cmd_device_list.go b/internal/app/enaptercli/cmd_device_list.go index f06f0b1..f7ab211 100644 --- a/internal/app/enaptercli/cmd_device_list.go +++ b/internal/app/enaptercli/cmd_device_list.go @@ -33,8 +33,9 @@ func (c *cmdDevicesList) Flags() []cli.Flag { flags := c.cmdDevices.Flags() return append(flags, &cli.MultiStringFlag{ Target: &cli.StringSliceFlag{ - Name: "expand", - Usage: "coma separated list of expanded device info", + Name: "expand", + Usage: "coma separated list of expanded device info (supported values: " + + "connectivity, manifest, properties, communication_info, site)", }, Destination: &c.expand, }, &cli.StringFlag{ diff --git a/internal/app/enaptercli/testdata/helps/enapter device command get b/internal/app/enaptercli/testdata/helps/enapter device command get index 8299cbd..05f19d5 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command get +++ b/internal/app/enaptercli/testdata/helps/enapter device command get @@ -10,7 +10,7 @@ OPTIONS: --verbose log extra details about operation (default: false) --device-id value, -d value device ID --execution-id value execution ID - --expand value [ --expand value ] coma separated list of expanded options + --expand value [ --expand value ] coma separated list of expanded options (supported values: log) --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter device get b/internal/app/enaptercli/testdata/helps/enapter device get index 43b24ed..1d23d75 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device get +++ b/internal/app/enaptercli/testdata/helps/enapter device get @@ -9,7 +9,7 @@ OPTIONS: --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) --device-id value, -d value device ID - --expand value [ --expand value ] coma separated list of expanded device info + --expand value [ --expand value ] coma separated list of expanded device info (supported values: connectivity, manifest, properties, communication_info, site) --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter device list b/internal/app/enaptercli/testdata/helps/enapter device list index f7951a0..30efb5c 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device list +++ b/internal/app/enaptercli/testdata/helps/enapter device list @@ -8,7 +8,7 @@ OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) - --expand value [ --expand value ] coma separated list of expanded device info + --expand value [ --expand value ] coma separated list of expanded device info (supported values: connectivity, manifest, properties, communication_info, site) --site-id value list devices from this site --help, -h show help From 37112492ae1c579c4a832d2c233a3a779ddd02a1 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 30 Jun 2025 13:29:53 +0300 Subject: [PATCH 48/88] feat: generate device communication config --- internal/app/enaptercli/cmd_device.go | 1 + .../cmd_device_communication_config.go | 48 +++++++++++++ ...md_device_communication_config_generate.go | 67 +++++++++++++++++++ .../enaptercli/testdata/helps/enapter device | 15 +++-- .../helps/enapter device communication-config | 18 +++++ ...apter device communication-config generate | 19 ++++++ 6 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 internal/app/enaptercli/cmd_device_communication_config.go create mode 100644 internal/app/enaptercli/cmd_device_communication_config_generate.go create mode 100644 internal/app/enaptercli/testdata/helps/enapter device communication-config create mode 100644 internal/app/enaptercli/testdata/helps/enapter device communication-config generate diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 90fb7b1..6e46b23 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -25,6 +25,7 @@ func buildCmdDevices() *cli.Command { buildCmdDevicesLogs(), buildCmdDevicesDelete(), buildCmdDeviceCommand(), + buildCmdDeviceCommunicationConfig(), }, } } diff --git a/internal/app/enaptercli/cmd_device_communication_config.go b/internal/app/enaptercli/cmd_device_communication_config.go new file mode 100644 index 0000000..6515224 --- /dev/null +++ b/internal/app/enaptercli/cmd_device_communication_config.go @@ -0,0 +1,48 @@ +package enaptercli + +import ( + "context" + "fmt" + "net/url" + + "github.com/urfave/cli/v2" +) + +type cmdDeviceCommunicationConfig struct { + cmdDevices + deviceID string +} + +func buildCmdDeviceCommunicationConfig() *cli.Command { + cmd := &cmdDeviceCommunicationConfig{} + return &cli.Command{ + Name: "communication-config", + Usage: "Manage device communication config", + CustomHelpTemplate: cmd.SubcommandHelpTemplate(), + Subcommands: []*cli.Command{ + buildCmdDeviceCommunicationConfigGenerate(), + }, + } +} + +func (c *cmdDeviceCommunicationConfig) Flags() []cli.Flag { + flags := c.cmdDevices.Flags() + return append(flags, + &cli.StringFlag{ + Name: "device-id", + Aliases: []string{"d"}, + Usage: "device ID", + Destination: &c.deviceID, + Required: true, + }, + ) +} + +func (c *cmdDeviceCommunicationConfig) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { + path, err := url.JoinPath(c.deviceID, p.Path) + if err != nil { + return fmt.Errorf("join path: %w", err) + } + p.Path = path + return c.cmdDevices.doHTTPRequest(ctx, p) +} diff --git a/internal/app/enaptercli/cmd_device_communication_config_generate.go b/internal/app/enaptercli/cmd_device_communication_config_generate.go new file mode 100644 index 0000000..1e3f821 --- /dev/null +++ b/internal/app/enaptercli/cmd_device_communication_config_generate.go @@ -0,0 +1,67 @@ +package enaptercli + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdDeviceCommunicationConfigGenerate struct { + cmdDeviceCommunicationConfig + protocol string +} + +func buildCmdDeviceCommunicationConfigGenerate() *cli.Command { + cmd := &cmdDeviceCommunicationConfigGenerate{} + return &cli.Command{ + Name: "generate", + Usage: "Generate a new communication config for device", + CustomHelpTemplate: cmd.CommandHelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdDeviceCommunicationConfigGenerate) Flags() []cli.Flag { + flags := c.cmdDeviceCommunicationConfig.Flags() + return append(flags, + &cli.StringFlag{ + Name: "protocol", + Usage: "connection protocol (supported values: MQTT, MQTTS)", + Destination: &c.protocol, + Required: true, + }, + ) +} + +// func (c *cmdDeviceCommandGet) Before(cliCtx *cli.Context) error { +// if err := c.cmdDevices.Before(cliCtx); err != nil { +// return err +// } +// return validateExpandFlag(cliCtx, []string{"log"}) +// } + +func (c *cmdDeviceCommunicationConfigGenerate) do(ctx context.Context) error { + reqBody := struct { + Protocol string `json:"protocol"` + }{ + Protocol: c.protocol, + } + data, err := json.Marshal(reqBody) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/generate_config", + Body: bytes.NewReader(data), + }) +} diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index fc16426..039415e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -5,13 +5,14 @@ USAGE: enaptercli.test device command [command options] COMMANDS: - list List user devices - get Get a device info - assign-blueprint Assign blueprint to device - logs Show device logs - delete Delete a device - command Manage device commands - help, h Shows a list of commands or help for one command + list List user devices + get Get a device info + assign-blueprint Assign blueprint to device + logs Show device logs + delete Delete a device + command Manage device commands + communication-config Manage device communication config + help, h Shows a list of commands or help for one command OPTIONS: --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter device communication-config b/internal/app/enaptercli/testdata/helps/enapter device communication-config new file mode 100644 index 0000000..4d878b3 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter device communication-config @@ -0,0 +1,18 @@ +NAME: + enaptercli.test device communication-config - Manage device communication config + +USAGE: + enaptercli.test device communication-config command [command options] + +COMMANDS: + generate Generate a new communication config for device + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + diff --git a/internal/app/enaptercli/testdata/helps/enapter device communication-config generate b/internal/app/enaptercli/testdata/helps/enapter device communication-config generate new file mode 100644 index 0000000..cbf6db8 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter device communication-config generate @@ -0,0 +1,19 @@ +NAME: + enaptercli.test device communication-config generate - Generate a new communication config for device + +USAGE: + enaptercli.test device communication-config generate [command options] + +OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about operation (default: false) + --device-id value, -d value device ID + --protocol value connection protocol (supported values: MQTT, MQTTS) + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + From a89e3a4babcf17d421228836af905a371b2906f5 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 1 Jul 2025 16:39:18 +0300 Subject: [PATCH 49/88] feat: delete default exec-interval value --- internal/app/cliflags/duration.go | 27 +++++++++++++++++++ .../enaptercli/cmd_rule_engine_rule_create.go | 12 ++++++--- .../cmd_rule_engine_rule_update_script.go | 12 ++++++--- .../helps/enapter rule-engine rule create | 2 +- .../enapter rule-engine rule update-script | 2 +- 5 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 internal/app/cliflags/duration.go diff --git a/internal/app/cliflags/duration.go b/internal/app/cliflags/duration.go new file mode 100644 index 0000000..cd68e37 --- /dev/null +++ b/internal/app/cliflags/duration.go @@ -0,0 +1,27 @@ +package cliflags + +import ( + "github.com/urfave/cli/v2" +) + +// Duration is a wrapper around cli.DurationFlag to implement cli.Flag interface. +// It differs from cli.DurationFlag in that it does not return a default text if the value is zero. +type Duration struct { + cli.DurationFlag +} + +var ( + _ cli.Flag = (*Duration)(nil) + _ cli.DocGenerationFlag = (*Duration)(nil) +) + +func (d *Duration) String() string { + return cli.FlagStringer(d) +} + +func (d *Duration) GetDefaultText() string { + if d.Value == 0 { + return "" + } + return d.DurationFlag.GetDefaultText() +} diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_create.go b/internal/app/enaptercli/cmd_rule_engine_rule_create.go index f7a6af3..ffe1fc0 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_create.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_create.go @@ -11,6 +11,8 @@ import ( "time" "github.com/urfave/cli/v2" + + "github.com/enapter/enapter-cli/internal/app/cliflags" ) type cmdRuleEngineRuleCreate struct { @@ -59,10 +61,12 @@ func (c *cmdRuleEngineRuleCreate) Flags() []cli.Flag { return c.validateRuntimeVersion(v) }, }, - &cli.DurationFlag{ - Name: "exec-interval", - Usage: "How often to execute the script. This option is only compatible with the runtime version 1", - Destination: &c.execInterval, + &cliflags.Duration{ + DurationFlag: cli.DurationFlag{ + Name: "exec-interval", + Usage: "How often to execute the script. This option is only compatible with the runtime version 1", + Destination: &c.execInterval, + }, }, &cli.BoolFlag{ Name: "disable", diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go index 3684f64..50c7760 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go @@ -11,6 +11,8 @@ import ( "time" "github.com/urfave/cli/v2" + + "github.com/enapter/enapter-cli/internal/app/cliflags" ) type cmdRuleEngineRuleUpdateScript struct { @@ -58,10 +60,12 @@ func (c *cmdRuleEngineRuleUpdateScript) Flags() []cli.Flag { return c.validateRuntimeVersion(v) }, }, - &cli.DurationFlag{ - Name: "exec-interval", - Usage: "How often to execute the script. This option is only compatible with the runtime version 1", - Destination: &c.execInterval, + &cliflags.Duration{ + DurationFlag: cli.DurationFlag{ + Name: "exec-interval", + Usage: "How often to execute the script. This option is only compatible with the runtime version 1", + Destination: &c.execInterval, + }, }, ) } diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create index 8af3d12..c0e22d7 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create @@ -11,7 +11,7 @@ OPTIONS: --slug value Slug of a new rule --script value Path to a file containing the script code --runtime-version value Version of a runtime to use for the script execution (default: "V3") - --exec-interval value How often to execute the script. This option is only compatible with the runtime version 1 (default: 0s) + --exec-interval value How often to execute the script. This option is only compatible with the runtime version 1 --disable Whether to disable a rule upon creation (default: false) --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script index 8009d49..ef66dec 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script @@ -11,7 +11,7 @@ OPTIONS: --rule-id value Rule ID or slug to update --script value Path to a file containing the script code --runtime-version value Version of a runtime to use for the script execution (default: "V3") - --exec-interval value How often to execute the script. This option is only compatible with the runtime version 1 (default: 0s) + --exec-interval value How often to execute the script. This option is only compatible with the runtime version 1 --help, -h show help ENVIRONMENT VARIABLES: From 2ea210c4d072f1ff68990498ffc3776109bf2365 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Tue, 1 Jul 2025 17:01:51 +0300 Subject: [PATCH 50/88] fix: make site list close to API --- internal/app/enaptercli/cmd_site_list.go | 31 ++++++++++--------- .../testdata/helps/enapter site list | 1 + 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/internal/app/enaptercli/cmd_site_list.go b/internal/app/enaptercli/cmd_site_list.go index feb785e..a1d56c8 100644 --- a/internal/app/enaptercli/cmd_site_list.go +++ b/internal/app/enaptercli/cmd_site_list.go @@ -3,16 +3,13 @@ package enaptercli import ( "context" "net/http" - "net/url" - "strconv" "github.com/urfave/cli/v2" ) type cmdSitesList struct { cmdSite - offset int - limit int + mySites bool } func buildCmdSitesList() *cli.Command { @@ -29,18 +26,22 @@ func buildCmdSitesList() *cli.Command { } } +func (c *cmdSitesList) Flags() []cli.Flag { + flags := c.cmdSite.Flags() + return append(flags, &cli.BoolFlag{ + Name: "my-sites", + Usage: "returns only sites where user is owner or installer", + Destination: &c.mySites, + }) +} + func (c *cmdSitesList) do(ctx context.Context) error { - query := url.Values{} - if c.offset != 0 { - query.Set("offset", strconv.Itoa(c.offset)) - } - if c.limit != 0 { - query.Set("limit", strconv.Itoa(c.limit)) + if c.mySites { + return c.cmdBase.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/users/me/sites", + }) } - return c.cmdBase.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodGet, - Path: "/users/me/sites", - Query: query, - }) + return c.cmdSite.doHTTPRequest(ctx, doHTTPRequestParams{Method: http.MethodGet}) } diff --git a/internal/app/enaptercli/testdata/helps/enapter site list b/internal/app/enaptercli/testdata/helps/enapter site list index c4adbe6..ef6fdf0 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site list +++ b/internal/app/enaptercli/testdata/helps/enapter site list @@ -8,6 +8,7 @@ OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about operation (default: false) + --my-sites returns only sites where user is owner or installer (default: false) --help, -h show help ENVIRONMENT VARIABLES: From 45a46e050099235fde0dfac7f83ad1c61cfbc8ca Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Wed, 2 Jul 2025 12:34:37 +0300 Subject: [PATCH 51/88] doc: update help messages --- internal/app/enaptercli/cmd_base.go | 8 ++++---- .../app/enaptercli/cmd_blueprints_download.go | 4 ++-- internal/app/enaptercli/cmd_blueprints_get.go | 4 ++-- .../app/enaptercli/cmd_blueprints_profiles.go | 2 +- .../cmd_blueprints_profiles_download.go | 4 ++-- .../cmd_blueprints_profiles_upload.go | 2 +- .../app/enaptercli/cmd_blueprints_upload.go | 2 +- .../app/enaptercli/cmd_device_command_get.go | 4 ++-- internal/app/enaptercli/cmd_device_get.go | 4 ++-- internal/app/enaptercli/cmd_device_list.go | 2 +- internal/app/enaptercli/cmd_device_logs.go | 8 ++++---- .../enaptercli/cmd_provisioning_lua_device.go | 8 ++++---- .../enaptercli/cmd_provisioning_standalone.go | 4 ++-- internal/app/enaptercli/cmd_rule_engine_get.go | 2 +- .../enaptercli/cmd_rule_engine_rule_create.go | 10 +++++----- .../app/enaptercli/cmd_rule_engine_rule_get.go | 2 +- .../enaptercli/cmd_rule_engine_rule_logs.go | 2 +- .../cmd_rule_engine_rule_update_script.go | 4 ++-- internal/app/enaptercli/execute.go | 6 +++--- internal/app/enaptercli/testdata/helps/enapter | 8 ++++---- .../testdata/helps/enapter blueprint | 12 ++++++------ .../testdata/helps/enapter blueprint download | 12 ++++++------ .../testdata/helps/enapter blueprint get | 12 ++++++------ .../testdata/helps/enapter blueprint profiles | 10 +++++----- .../helps/enapter blueprint profiles download | 12 ++++++------ .../helps/enapter blueprint profiles upload | 10 +++++----- .../testdata/helps/enapter blueprint upload | 10 +++++----- .../enaptercli/testdata/helps/enapter device | 6 +++--- .../helps/enapter device assign-blueprint | 8 ++++---- .../testdata/helps/enapter device command | 6 +++--- .../helps/enapter device command execute | 8 ++++---- .../testdata/helps/enapter device command get | 12 ++++++------ .../testdata/helps/enapter device command list | 8 ++++---- .../helps/enapter device communication-config | 4 ++-- ...napter device communication-config generate | 8 ++++---- .../testdata/helps/enapter device delete | 8 ++++---- .../testdata/helps/enapter device get | 12 ++++++------ .../testdata/helps/enapter device list | 10 +++++----- .../testdata/helps/enapter device logs | 16 ++++++++-------- .../testdata/helps/enapter provisioning | 4 ++-- .../helps/enapter provisioning lua-device | 16 ++++++++-------- .../helps/enapter provisioning standalone | 12 ++++++------ .../testdata/helps/enapter rule-engine | 6 +++--- .../testdata/helps/enapter rule-engine get | 10 +++++----- .../testdata/helps/enapter rule-engine resume | 8 ++++---- .../testdata/helps/enapter rule-engine rule | 6 +++--- .../helps/enapter rule-engine rule create | 18 +++++++++--------- .../helps/enapter rule-engine rule delete | 8 ++++---- .../helps/enapter rule-engine rule disable | 8 ++++---- .../helps/enapter rule-engine rule enable | 8 ++++---- .../helps/enapter rule-engine rule get | 10 +++++----- .../helps/enapter rule-engine rule list | 8 ++++---- .../helps/enapter rule-engine rule logs | 10 +++++----- .../helps/enapter rule-engine rule update | 8 ++++---- .../enapter rule-engine rule update-script | 12 ++++++------ .../testdata/helps/enapter rule-engine suspend | 8 ++++---- .../app/enaptercli/testdata/helps/enapter site | 4 ++-- .../enaptercli/testdata/helps/enapter site get | 8 ++++---- .../testdata/helps/enapter site list | 8 ++++---- 59 files changed, 227 insertions(+), 227 deletions(-) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index bb9dea4..876f5e4 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -50,13 +50,13 @@ func (c *cmdBase) Flags() []cli.Flag { }, &cli.BoolFlag{ Name: "api-allow-insecure", - Usage: "allow insecure connections to Enapter API", + Usage: "allow insecure connections to the Enapter API", EnvVars: []string{"ENAPTER3_API_ALLOW_INSECURE"}, Destination: &c.apiAllowInsecure, }, &cli.BoolFlag{ Name: "verbose", - Usage: "log extra details about operation", + Usage: "log extra details about the operation", Destination: &c.verbose, }, } @@ -80,8 +80,8 @@ func (c *cmdBase) Before(cliCtx *cli.Context) error { const enapterAPIEnvVarsHelp = ` ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) ` diff --git a/internal/app/enaptercli/cmd_blueprints_download.go b/internal/app/enaptercli/cmd_blueprints_download.go index c92d71e..ff2efa9 100644 --- a/internal/app/enaptercli/cmd_blueprints_download.go +++ b/internal/app/enaptercli/cmd_blueprints_download.go @@ -22,7 +22,7 @@ func buildCmdBlueprintsDownload() *cli.Command { cmd := &cmdBlueprintsDownload{} return &cli.Command{ Name: "download", - Usage: "Download blueprint zip from Platform", + Usage: "Download the blueprint zip from the Platform", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, @@ -43,7 +43,7 @@ func (c *cmdBlueprintsDownload) Flags() []cli.Flag { }, &cli.StringFlag{ Name: "output", Aliases: []string{"o"}, - Usage: "blueprint file name to save", + Usage: "blueprint file name to save the blueprint", Destination: &c.outputFileName, }) } diff --git a/internal/app/enaptercli/cmd_blueprints_get.go b/internal/app/enaptercli/cmd_blueprints_get.go index ddce15d..03f6cea 100644 --- a/internal/app/enaptercli/cmd_blueprints_get.go +++ b/internal/app/enaptercli/cmd_blueprints_get.go @@ -16,7 +16,7 @@ func buildCmdBlueprintsGet() *cli.Command { cmd := &cmdBlueprintsGet{} return &cli.Command{ Name: "get", - Usage: "Get blueprint metadata", + Usage: "Retrieve blueprint metadata", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, @@ -31,7 +31,7 @@ func (c *cmdBlueprintsGet) Flags() []cli.Flag { return append(flags, &cli.StringFlag{ Name: "blueprint-id", Aliases: []string{"b"}, - Usage: "blueprint name or ID to get", + Usage: "blueprint name or ID to retrieve", Destination: &c.blueprintID, Required: true, }) diff --git a/internal/app/enaptercli/cmd_blueprints_profiles.go b/internal/app/enaptercli/cmd_blueprints_profiles.go index 1a72e03..0fcdb0e 100644 --- a/internal/app/enaptercli/cmd_blueprints_profiles.go +++ b/internal/app/enaptercli/cmd_blueprints_profiles.go @@ -12,7 +12,7 @@ func buildCmdBlueprintsProfiles() *cli.Command { cmd := &cmdBlueprintsProfiles{} return &cli.Command{ Name: "profiles", - Usage: "Manage blueprints profiles", + Usage: "Manage blueprint profiles", CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ buildCmdBlueprintsProfilesDownload(), diff --git a/internal/app/enaptercli/cmd_blueprints_profiles_download.go b/internal/app/enaptercli/cmd_blueprints_profiles_download.go index 66b2dea..5efd9ee 100644 --- a/internal/app/enaptercli/cmd_blueprints_profiles_download.go +++ b/internal/app/enaptercli/cmd_blueprints_profiles_download.go @@ -19,7 +19,7 @@ func buildCmdBlueprintsProfilesDownload() *cli.Command { cmd := &cmdBlueprintsProfilesDownload{} return &cli.Command{ Name: "download", - Usage: "Download profiles zip from Platform", + Usage: "Download profiles zip from the Platform", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, @@ -34,7 +34,7 @@ func (c *cmdBlueprintsProfilesDownload) Flags() []cli.Flag { return append(flags, &cli.StringFlag{ Name: "output", Aliases: []string{"o"}, - Usage: "file name to save", + Usage: "file name to save the downloaded profiles", Destination: &c.outputFileName, }) } diff --git a/internal/app/enaptercli/cmd_blueprints_profiles_upload.go b/internal/app/enaptercli/cmd_blueprints_profiles_upload.go index 70a277f..719ce06 100644 --- a/internal/app/enaptercli/cmd_blueprints_profiles_upload.go +++ b/internal/app/enaptercli/cmd_blueprints_profiles_upload.go @@ -19,7 +19,7 @@ func buildCmdBlueprintsProfilesUpload() *cli.Command { cmd := &cmdBlueprintsProfilesUpload{} return &cli.Command{ Name: "upload", - Usage: "Upload profiles into Platform", + Usage: "Upload profiles to the Platform", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, diff --git a/internal/app/enaptercli/cmd_blueprints_upload.go b/internal/app/enaptercli/cmd_blueprints_upload.go index d2bc18e..e3a2e79 100644 --- a/internal/app/enaptercli/cmd_blueprints_upload.go +++ b/internal/app/enaptercli/cmd_blueprints_upload.go @@ -20,7 +20,7 @@ func buildCmdBlueprintsUpload() *cli.Command { cmd := &cmdBlueprintsUpload{} return &cli.Command{ Name: "upload", - Usage: "Upload blueprint into Platform", + Usage: "Upload the blueprint to the Platform", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, diff --git a/internal/app/enaptercli/cmd_device_command_get.go b/internal/app/enaptercli/cmd_device_command_get.go index a929d42..af90b31 100644 --- a/internal/app/enaptercli/cmd_device_command_get.go +++ b/internal/app/enaptercli/cmd_device_command_get.go @@ -19,7 +19,7 @@ func buildCmdDeviceCommandGet() *cli.Command { cmd := &cmdDeviceCommandGet{} return &cli.Command{ Name: "get", - Usage: "Get a device command execution", + Usage: "Retrieve a device command execution", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, @@ -40,7 +40,7 @@ func (c *cmdDeviceCommandGet) Flags() []cli.Flag { }, &cli.MultiStringFlag{ Target: &cli.StringSliceFlag{ Name: "expand", - Usage: "coma separated list of expanded options (supported values: log)", + Usage: "coma-separated list of expanded options (supported values: log)", }, Destination: &c.expand, }, diff --git a/internal/app/enaptercli/cmd_device_get.go b/internal/app/enaptercli/cmd_device_get.go index f4aea47..3d754eb 100644 --- a/internal/app/enaptercli/cmd_device_get.go +++ b/internal/app/enaptercli/cmd_device_get.go @@ -19,7 +19,7 @@ func buildCmdDevicesGet() *cli.Command { cmd := &cmdDevicesGet{} return &cli.Command{ Name: "get", - Usage: "Get a device info", + Usage: "Retrieve device information", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, @@ -40,7 +40,7 @@ func (c *cmdDevicesGet) Flags() []cli.Flag { }, &cli.MultiStringFlag{ Target: &cli.StringSliceFlag{ Name: "expand", - Usage: "coma separated list of expanded device info (supported values: " + + Usage: "coma-separated list of expanded device information (supported values: " + "connectivity, manifest, properties, communication_info, site)", }, Destination: &c.expand, diff --git a/internal/app/enaptercli/cmd_device_list.go b/internal/app/enaptercli/cmd_device_list.go index f7ab211..2b90284 100644 --- a/internal/app/enaptercli/cmd_device_list.go +++ b/internal/app/enaptercli/cmd_device_list.go @@ -34,7 +34,7 @@ func (c *cmdDevicesList) Flags() []cli.Flag { return append(flags, &cli.MultiStringFlag{ Target: &cli.StringSliceFlag{ Name: "expand", - Usage: "coma separated list of expanded device info (supported values: " + + Usage: "coma-separated list of expanded device information (supported values: " + "connectivity, manifest, properties, communication_info, site)", }, Destination: &c.expand, diff --git a/internal/app/enaptercli/cmd_device_logs.go b/internal/app/enaptercli/cmd_device_logs.go index ad615c7..c0621b5 100644 --- a/internal/app/enaptercli/cmd_device_logs.go +++ b/internal/app/enaptercli/cmd_device_logs.go @@ -51,16 +51,16 @@ func (c *cmdDevicesLogs) Flags() []cli.Flag { }, &cli.BoolFlag{ Name: "follow", Aliases: []string{"f"}, - Usage: "follow log output", + Usage: "follow the log output", Destination: &c.follow, }, &cli.TimestampFlag{ Name: "from", - Usage: "from timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z)", + Usage: "from timestamp in RFC 3339 format (e.g. 2006-01-02T15:04:05Z)", Destination: &c.from, Layout: time.RFC3339, }, &cli.TimestampFlag{ Name: "to", - Usage: "to timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z)", + Usage: "to timestamp in RFC 3339 format (e.g. 2006-01-02T15:04:05Z)", Destination: &c.to, Layout: time.RFC3339, }, &cli.IntFlag{ @@ -71,7 +71,7 @@ func (c *cmdDevicesLogs) Flags() []cli.Flag { }, &cli.IntFlag{ Name: "offset", Aliases: []string{"o"}, - Usage: "number of logs to skip on retrieve", + Usage: "number of logs to skip when retrieving", Destination: &c.offset, }, &cli.StringFlag{ Name: "severity", diff --git a/internal/app/enaptercli/cmd_provisioning_lua_device.go b/internal/app/enaptercli/cmd_provisioning_lua_device.go index 06ddde0..443b1f0 100644 --- a/internal/app/enaptercli/cmd_provisioning_lua_device.go +++ b/internal/app/enaptercli/cmd_provisioning_lua_device.go @@ -38,23 +38,23 @@ func (c *cmdProvisioningLua) Flags() []cli.Flag { return append(flags, &cli.StringFlag{ Name: "runtime-id", Aliases: []string{"r"}, - Usage: "runtime UCM device ID where to run a new Lua device", + Usage: "UCM device ID where the new Lua device will run", Destination: &c.runtimeID, Required: true, }, &cli.StringFlag{ Name: "device-name", Aliases: []string{"n"}, - Usage: "name of a new Lua device", + Usage: "name for the new Lua device", Destination: &c.deviceName, Required: true, }, &cli.StringFlag{ Name: "device-slug", - Usage: "slug of a new Lua device", + Usage: "slug for the new Lua device", Destination: &c.deviceSlug, }, &cli.StringFlag{ Name: "blueprint-id", Aliases: []string{"b"}, - Usage: "blueprint ID of a new Lua device", + Usage: "blueprint ID for the new Lua device", Destination: &c.blueprintID, }, &cli.StringFlag{ Name: "blueprint-path", diff --git a/internal/app/enaptercli/cmd_provisioning_standalone.go b/internal/app/enaptercli/cmd_provisioning_standalone.go index 0ef7f13..b295221 100644 --- a/internal/app/enaptercli/cmd_provisioning_standalone.go +++ b/internal/app/enaptercli/cmd_provisioning_standalone.go @@ -35,12 +35,12 @@ func (c *cmdProvisioningStandalone) Flags() []cli.Flag { return append(flags, &cli.StringFlag{ Name: "site-id", Aliases: []string{"s"}, - Usage: "site ID where to craate device", + Usage: "site ID where the device will be created", Destination: &c.siteID, }, &cli.StringFlag{ Name: "device-name", Aliases: []string{"n"}, - Usage: "name for a new device", + Usage: "name for the new device", Destination: &c.deviceName, Required: true, }) diff --git a/internal/app/enaptercli/cmd_rule_engine_get.go b/internal/app/enaptercli/cmd_rule_engine_get.go index 88c5c4d..69a9bf4 100644 --- a/internal/app/enaptercli/cmd_rule_engine_get.go +++ b/internal/app/enaptercli/cmd_rule_engine_get.go @@ -15,7 +15,7 @@ func buildCmdRuleEngineGet() *cli.Command { cmd := &cmdRuleEngineGet{} return &cli.Command{ Name: "get", - Usage: "Get the rule engine", + Usage: "Retrieve the rule engine", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_create.go b/internal/app/enaptercli/cmd_rule_engine_rule_create.go index ffe1fc0..fa23d3b 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_create.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_create.go @@ -42,19 +42,19 @@ func (c *cmdRuleEngineRuleCreate) Flags() []cli.Flag { return append(c.cmdRuleEngineRule.Flags(), &cli.StringFlag{ Name: "slug", - Usage: "Slug of a new rule", + Usage: "Slug for the new rule", Destination: &c.slug, Required: true, }, &cli.StringFlag{ Name: "script", - Usage: "Path to a file containing the script code", + Usage: "Path to the file containing the script code", Destination: &c.scriptPath, Required: true, }, &cli.StringFlag{ Name: "runtime-version", - Usage: "Version of a runtime to use for the script execution", + Usage: "Version of the runtime to use for the script execution", Destination: &c.runtimeVersion, Value: ruleRuntimeV3, Action: func(_ *cli.Context, v string) error { @@ -64,13 +64,13 @@ func (c *cmdRuleEngineRuleCreate) Flags() []cli.Flag { &cliflags.Duration{ DurationFlag: cli.DurationFlag{ Name: "exec-interval", - Usage: "How often to execute the script. This option is only compatible with the runtime version 1", + Usage: "How often to execute the script (only compatible with the runtime version 1)", Destination: &c.execInterval, }, }, &cli.BoolFlag{ Name: "disable", - Usage: "Whether to disable a rule upon creation", + Usage: "Disable the rule upon creation", Destination: &c.disable, }, ) diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_get.go b/internal/app/enaptercli/cmd_rule_engine_rule_get.go index 574600d..abf31f1 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_get.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_get.go @@ -16,7 +16,7 @@ func buildCmdRuleEngineRuleGet() *cli.Command { cmd := &cmdRuleEngineRuleGet{} return &cli.Command{ Name: "get", - Usage: "Get a rule", + Usage: "Retrieve a rule", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_logs.go b/internal/app/enaptercli/cmd_rule_engine_rule_logs.go index 53609cf..6e328ee 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_logs.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_logs.go @@ -40,7 +40,7 @@ func (c *cmdRuleEngineRuleLogs) Flags() []cli.Flag { &cli.BoolFlag{ Name: "follow", Aliases: []string{"f"}, - Usage: "follow log output", + Usage: "follow the log output", Destination: &c.follow, }, ) diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go index 50c7760..8d04311 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go @@ -53,7 +53,7 @@ func (c *cmdRuleEngineRuleUpdateScript) Flags() []cli.Flag { }, &cli.StringFlag{ Name: "runtime-version", - Usage: "Version of a runtime to use for the script execution", + Usage: "Version of the runtime to use for the script execution", Destination: &c.runtimeVersion, Value: ruleRuntimeV3, Action: func(_ *cli.Context, v string) error { @@ -63,7 +63,7 @@ func (c *cmdRuleEngineRuleUpdateScript) Flags() []cli.Flag { &cliflags.Duration{ DurationFlag: cli.DurationFlag{ Name: "exec-interval", - Usage: "How often to execute the script. This option is only compatible with the runtime version 1", + Usage: "How often to execute the script (only compatible with the runtime version 1)", Destination: &c.execInterval, }, }, diff --git a/internal/app/enaptercli/execute.go b/internal/app/enaptercli/execute.go index 4c5a9ad..89f76e4 100644 --- a/internal/app/enaptercli/execute.go +++ b/internal/app/enaptercli/execute.go @@ -16,9 +16,9 @@ import ( func NewApp() *cli.App { app := cli.NewApp() - app.Usage = "Command line interface for Enapter services." - app.Description = "Enapter CLI requires access token for authentication. " + - "The token can be obtained in your Enapter Cloud account settings." + app.Usage = "Command Line Interface (CLI) for Enapter services." + app.Description = "The Enapter CLI requires an access token for authentication. " + + "You can obtain the token in your Enapter Cloud account settings." app.CustomAppHelpTemplate = cli.AppHelpTemplate + enapterAPIEnvVarsHelp app.Commands = []*cli.Command{ diff --git a/internal/app/enaptercli/testdata/helps/enapter b/internal/app/enaptercli/testdata/helps/enapter index 43e4ef5..8f11668 100644 --- a/internal/app/enaptercli/testdata/helps/enapter +++ b/internal/app/enaptercli/testdata/helps/enapter @@ -1,11 +1,11 @@ NAME: - enaptercli.test - Command line interface for Enapter services. + enaptercli.test - Command Line Interface (CLI) for Enapter services. USAGE: enaptercli.test [global options] command [command options] DESCRIPTION: - Enapter CLI requires access token for authentication. The token can be obtained in your Enapter Cloud account settings. + The Enapter CLI requires an access token for authentication. You can obtain the token in your Enapter Cloud account settings. COMMANDS: site Manage sites @@ -20,6 +20,6 @@ GLOBAL OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint b/internal/app/enaptercli/testdata/helps/enapter blueprint index 7c70f40..4c25e20 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint @@ -5,10 +5,10 @@ USAGE: enaptercli.test blueprint command [command options] COMMANDS: - profiles Manage blueprints profiles - upload Upload blueprint into Platform - download Download blueprint zip from Platform - get Get blueprint metadata + profiles Manage blueprint profiles + upload Upload the blueprint to the Platform + download Download the blueprint zip from the Platform + get Retrieve blueprint metadata help, h Shows a list of commands or help for one command OPTIONS: @@ -16,6 +16,6 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint download b/internal/app/enaptercli/testdata/helps/enapter blueprint download index b298078..d59236d 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint download @@ -1,19 +1,19 @@ NAME: - enaptercli.test blueprint download - Download blueprint zip from Platform + enaptercli.test blueprint download - Download the blueprint zip from the Platform USAGE: enaptercli.test blueprint download [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --blueprint-id value, -b value blueprint name or ID to download - --output value, -o value blueprint file name to save + --output value, -o value blueprint file name to save the blueprint --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint get b/internal/app/enaptercli/testdata/helps/enapter blueprint get index 3a6cc02..6dfa405 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint get +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint get @@ -1,18 +1,18 @@ NAME: - enaptercli.test blueprint get - Get blueprint metadata + enaptercli.test blueprint get - Retrieve blueprint metadata USAGE: enaptercli.test blueprint get [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) - --blueprint-id value, -b value blueprint name or ID to get + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --blueprint-id value, -b value blueprint name or ID to retrieve --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles index 48597a6..e8d0dde 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles @@ -1,12 +1,12 @@ NAME: - enaptercli.test blueprint profiles - Manage blueprints profiles + enaptercli.test blueprint profiles - Manage blueprint profiles USAGE: enaptercli.test blueprint profiles command [command options] COMMANDS: - download Download profiles zip from Platform - upload Upload profiles into Platform + download Download profiles zip from the Platform + upload Upload profiles to the Platform help, h Shows a list of commands or help for one command OPTIONS: @@ -14,6 +14,6 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download index b6dcf1b..1342fdb 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download @@ -1,18 +1,18 @@ NAME: - enaptercli.test blueprint profiles download - Download profiles zip from Platform + enaptercli.test blueprint profiles download - Download profiles zip from the Platform USAGE: enaptercli.test blueprint profiles download [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) - --output value, -o value file name to save + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --output value, -o value file name to save the downloaded profiles --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload index 2c9b1d0..c3ac04e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload @@ -1,18 +1,18 @@ NAME: - enaptercli.test blueprint profiles upload - Upload profiles into Platform + enaptercli.test blueprint profiles upload - Upload profiles to the Platform USAGE: enaptercli.test blueprint profiles upload [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --path value, -p value profiles zip file path --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint upload b/internal/app/enaptercli/testdata/helps/enapter blueprint upload index eefa2ba..e6af592 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint upload @@ -1,18 +1,18 @@ NAME: - enaptercli.test blueprint upload - Upload blueprint into Platform + enaptercli.test blueprint upload - Upload the blueprint to the Platform USAGE: enaptercli.test blueprint upload [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --path value, -p value blueprint path (zip file or directory) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index 039415e..52bc356 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -6,7 +6,7 @@ USAGE: COMMANDS: list List user devices - get Get a device info + get Retrieve device information assign-blueprint Assign blueprint to device logs Show device logs delete Delete a device @@ -19,6 +19,6 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint b/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint index f6f6edc..c191e45 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint @@ -6,8 +6,8 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --device-id value, -d value device ID --blueprint-id value, -b value blueprint ID to assign --blueprint-path value blueprint path (zip file or directory) to assign @@ -15,6 +15,6 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device command b/internal/app/enaptercli/testdata/helps/enapter device command index 46a20bc..a29328a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command +++ b/internal/app/enaptercli/testdata/helps/enapter device command @@ -7,7 +7,7 @@ USAGE: COMMANDS: execute Execute a device command list List device command executions - get Get a device command execution + get Retrieve a device command execution help, h Shows a list of commands or help for one command OPTIONS: @@ -15,6 +15,6 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device command execute b/internal/app/enaptercli/testdata/helps/enapter device command execute index 0dd6c3b..b23a949 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command execute +++ b/internal/app/enaptercli/testdata/helps/enapter device command execute @@ -6,8 +6,8 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --device-id value, -d value device ID --name value command name --arguments value command arguments (should be a JSON string) @@ -15,6 +15,6 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device command get b/internal/app/enaptercli/testdata/helps/enapter device command get index 05f19d5..8926ecd 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command get +++ b/internal/app/enaptercli/testdata/helps/enapter device command get @@ -1,20 +1,20 @@ NAME: - enaptercli.test device command get - Get a device command execution + enaptercli.test device command get - Retrieve a device command execution USAGE: enaptercli.test device command get [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --device-id value, -d value device ID --execution-id value execution ID - --expand value [ --expand value ] coma separated list of expanded options (supported values: log) + --expand value [ --expand value ] coma-separated list of expanded options (supported values: log) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device command list b/internal/app/enaptercli/testdata/helps/enapter device command list index 269e351..af59597 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command list +++ b/internal/app/enaptercli/testdata/helps/enapter device command list @@ -6,13 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --device-id value, -d value device ID --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device communication-config b/internal/app/enaptercli/testdata/helps/enapter device communication-config index 4d878b3..9ca4f7f 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device communication-config +++ b/internal/app/enaptercli/testdata/helps/enapter device communication-config @@ -13,6 +13,6 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device communication-config generate b/internal/app/enaptercli/testdata/helps/enapter device communication-config generate index cbf6db8..8086f54 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device communication-config generate +++ b/internal/app/enaptercli/testdata/helps/enapter device communication-config generate @@ -6,14 +6,14 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --device-id value, -d value device ID --protocol value connection protocol (supported values: MQTT, MQTTS) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device delete b/internal/app/enaptercli/testdata/helps/enapter device delete index c4e1dbb..f14a7a5 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device delete +++ b/internal/app/enaptercli/testdata/helps/enapter device delete @@ -6,13 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --device-id value, -d value device ID --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device get b/internal/app/enaptercli/testdata/helps/enapter device get index 1d23d75..87491ab 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device get +++ b/internal/app/enaptercli/testdata/helps/enapter device get @@ -1,19 +1,19 @@ NAME: - enaptercli.test device get - Get a device info + enaptercli.test device get - Retrieve device information USAGE: enaptercli.test device get [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --device-id value, -d value device ID - --expand value [ --expand value ] coma separated list of expanded device info (supported values: connectivity, manifest, properties, communication_info, site) + --expand value [ --expand value ] coma-separated list of expanded device information (supported values: connectivity, manifest, properties, communication_info, site) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device list b/internal/app/enaptercli/testdata/helps/enapter device list index 30efb5c..cbd161a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device list +++ b/internal/app/enaptercli/testdata/helps/enapter device list @@ -6,14 +6,14 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) - --expand value [ --expand value ] coma separated list of expanded device info (supported values: connectivity, manifest, properties, communication_info, site) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --expand value [ --expand value ] coma-separated list of expanded device information (supported values: connectivity, manifest, properties, communication_info, site) --site-id value list devices from this site --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter device logs b/internal/app/enaptercli/testdata/helps/enapter device logs index 27c6288..717d7f3 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device logs +++ b/internal/app/enaptercli/testdata/helps/enapter device logs @@ -6,14 +6,14 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --device-id value, -d value device ID - --follow, -f follow log output (default: false) - --from value from timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z) - --to value to timestamp in rfc 3339 format (like 2006-01-02T15:04:05Z) + --follow, -f follow the log output (default: false) + --from value from timestamp in RFC 3339 format (e.g. 2006-01-02T15:04:05Z) + --to value to timestamp in RFC 3339 format (e.g. 2006-01-02T15:04:05Z) --limit value, -l value maximum number of logs to retrieve (default: 0) - --offset value, -o value number of logs to skip on retrieve (default: 0) + --offset value, -o value number of logs to skip when retrieving (default: 0) --severity value, -s value filter logs by severity --order value order logs by criteria (RECEIVED_AT_ASC[default], RECEIVED_AT_DESC) --show value filter logs by criteria (ALL[default], PERSISTED_ONLY, TEMPORARY_ONLY) @@ -21,6 +21,6 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning b/internal/app/enaptercli/testdata/helps/enapter provisioning index 60cd959..4ae26f9 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning @@ -14,6 +14,6 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device b/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device index 976cbb4..c9c95b3 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device @@ -6,17 +6,17 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) - --runtime-id value, -r value runtime UCM device ID where to run a new Lua device - --device-name value, -n value name of a new Lua device - --device-slug value slug of a new Lua device - --blueprint-id value, -b value blueprint ID of a new Lua device + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --runtime-id value, -r value UCM device ID where the new Lua device will run + --device-name value, -n value name for the new Lua device + --device-slug value slug for the new Lua device + --blueprint-id value, -b value blueprint ID for the new Lua device --blueprint-path value blueprint path (zip file or directory) to assign --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone index 00c0d34..76e9215 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone +++ b/internal/app/enaptercli/testdata/helps/enapter provisioning standalone @@ -6,14 +6,14 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) - --site-id value, -s value site ID where to craate device - --device-name value, -n value name for a new device + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --site-id value, -s value site ID where the device will be created + --device-name value, -n value name for the new device --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine b/internal/app/enaptercli/testdata/helps/enapter rule-engine index 0e09d94..597bb25 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine @@ -5,7 +5,7 @@ USAGE: enaptercli.test rule-engine command [command options] COMMANDS: - get Get the rule engine + get Retrieve the rule engine suspend Suspend execution of rules resume Resume execution of rules rule Manage rules @@ -16,6 +16,6 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine get b/internal/app/enaptercli/testdata/helps/enapter rule-engine get index b427e6f..4fb579f 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine get +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine get @@ -1,17 +1,17 @@ NAME: - enaptercli.test rule-engine get - Get the rule engine + enaptercli.test rule-engine get - Retrieve the rule engine USAGE: enaptercli.test rule-engine get [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume index 9994e7e..c0a9858 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume @@ -6,12 +6,12 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule index 1db01d3..462f57c 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule @@ -9,7 +9,7 @@ COMMANDS: delete Delete one or more rules disable Disable one or more rules enable Enable one or more rules - get Get a rule + get Retrieve a rule list List rules update Update a rule update-script Update the script of a rule @@ -21,6 +21,6 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create index c0e22d7..c7ab5eb 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create @@ -6,17 +6,17 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) - --slug value Slug of a new rule - --script value Path to a file containing the script code - --runtime-version value Version of a runtime to use for the script execution (default: "V3") - --exec-interval value How often to execute the script. This option is only compatible with the runtime version 1 - --disable Whether to disable a rule upon creation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --slug value Slug for the new rule + --script value Path to the file containing the script code + --runtime-version value Version of the runtime to use for the script execution (default: "V3") + --exec-interval value How often to execute the script (only compatible with the runtime version 1) + --disable Disable the rule upon creation (default: false) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete index 7d4de28..b25fcab 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete @@ -6,13 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable index d34f98f..c6eaa23 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable @@ -6,13 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable index bee9481..20e3125 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable @@ -6,13 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get index 8b2e07f..b590be4 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get @@ -1,18 +1,18 @@ NAME: - enaptercli.test rule-engine rule get - Get a rule + enaptercli.test rule-engine rule get - Retrieve a rule USAGE: enaptercli.test rule-engine rule get [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --rule-id value Rule ID or slug --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list index 675f647..7804fa4 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list @@ -6,12 +6,12 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs index 1d2a4a0..a23a2a1 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs @@ -6,14 +6,14 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --rule-id value rule ID - --follow, -f follow log output (default: false) + --follow, -f follow the log output (default: false) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update index b5a5aae..2979698 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update @@ -6,14 +6,14 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --rule-id value Rule ID or slug to update --slug value A new rule slug --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script index ef66dec..89c2a9a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script @@ -6,16 +6,16 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --rule-id value Rule ID or slug to update --script value Path to a file containing the script code - --runtime-version value Version of a runtime to use for the script execution (default: "V3") - --exec-interval value How often to execute the script. This option is only compatible with the runtime version 1 + --runtime-version value Version of the runtime to use for the script execution (default: "V3") + --exec-interval value How often to execute the script (only compatible with the runtime version 1) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend index 282117b..60e5168 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend @@ -6,12 +6,12 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter site b/internal/app/enaptercli/testdata/helps/enapter site index 3f47ff6..e0cc024 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site +++ b/internal/app/enaptercli/testdata/helps/enapter site @@ -14,6 +14,6 @@ OPTIONS: ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter site get b/internal/app/enaptercli/testdata/helps/enapter site get index 6d61a7e..6c683f6 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site get +++ b/internal/app/enaptercli/testdata/helps/enapter site get @@ -6,13 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --site-id value site ID --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) diff --git a/internal/app/enaptercli/testdata/helps/enapter site list b/internal/app/enaptercli/testdata/helps/enapter site list index ef6fdf0..50944fc 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site list +++ b/internal/app/enaptercli/testdata/helps/enapter site list @@ -6,13 +6,13 @@ USAGE: OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about operation (default: false) + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) --my-sites returns only sites where user is owner or installer (default: false) --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token - ENAPTER3_API_URL Enapter API base URL (https://api.enapter.com by default) - ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to Enapter API (default false) + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) From fb288108eae87142e60a343be5f9705adf2a937b Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Wed, 9 Jul 2025 11:11:47 +0300 Subject: [PATCH 52/88] feat: suggest --api-allow-insecure option --- internal/app/enaptercli/cmd_base.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 876f5e4..a1053ea 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -124,6 +124,9 @@ func (c *cmdBase) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) erro resp, err := c.httpClient.Do(req) if err != nil { + if e := (&tls.CertificateVerificationError{}); errors.As(err, &e) { + return fmt.Errorf("do http request: %w (try to use --api-allow-insecure)", err) + } return fmt.Errorf("do http request: %w", err) } defer resp.Body.Close() From 6bfee06d483827fec1073350b8a811428322d0c7 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Wed, 9 Jul 2025 11:52:58 +0300 Subject: [PATCH 53/88] feat: support non-json error response --- internal/app/enaptercli/cmd_base.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index a1053ea..82c8432 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -285,9 +285,11 @@ func parseRespErrorMessage(resp *http.Response) string { Message string `json:"message"` } `json:"errors"` } - if err := json.NewDecoder(resp.Body).Decode(&errs); err != nil { + bodyBytes, _ := io.ReadAll(resp.Body) + if err := json.Unmarshal(bodyBytes, &errs); err != nil { if !errors.Is(err, io.EOF) { - return fmt.Sprintf("parse error response: %s", err) + return fmt.Sprintf("Request finished with HTTP status %q, but body is not valid JSON error response. "+ + "Please, check API URL is correct.\n\nReceived body:\n%s\n", resp.Status, bodyBytes) } } @@ -298,7 +300,7 @@ func parseRespErrorMessage(resp *http.Response) string { } } - return fmt.Sprintf("request finished with HTTP status %q, but without error message", resp.Status) + return fmt.Sprintf("Request finished with HTTP status %q, but without error message", resp.Status) } func validateExpandFlag(cliCtx *cli.Context, supportedFields []string) error { From 551bdf39dd6b8602df7ef2104eb03c4bcdae08e8 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Thu, 10 Jul 2025 10:06:03 +0300 Subject: [PATCH 54/88] feat: support pagination for devices and sites lists --- .../app/enaptercli/cmd_base_pagination.go | 123 ++++++++++++++++++ internal/app/enaptercli/cmd_device_list.go | 30 +++-- internal/app/enaptercli/cmd_site_list.go | 23 +++- .../testdata/helps/enapter device list | 1 + .../testdata/helps/enapter site list | 1 + 5 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 internal/app/enaptercli/cmd_base_pagination.go diff --git a/internal/app/enaptercli/cmd_base_pagination.go b/internal/app/enaptercli/cmd_base_pagination.go new file mode 100644 index 0000000..f8318f7 --- /dev/null +++ b/internal/app/enaptercli/cmd_base_pagination.go @@ -0,0 +1,123 @@ +package enaptercli + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strconv" + + "github.com/urfave/cli/v2" +) + +var errEndPagination = errors.New("end pagination") + +type paginateHTTPRequestParams struct { + BaseParams doHTTPRequestParams + DoFn func(ctx context.Context, p doHTTPRequestParams) error + Limit int + ObjectName string +} + +func (c *cmdBase) doPaginateRequest(ctx context.Context, p paginateHTTPRequestParams) error { + const maxPageLimit = 50 + if p.BaseParams.Query == nil { + p.BaseParams.Query = url.Values{} + } + if p.Limit > 0 && p.Limit < maxPageLimit { + p.BaseParams.Query.Set("limit", strconv.Itoa(p.Limit)) + return p.DoFn(ctx, p.BaseParams) + } + + paginateRespProcesor := &paginateRespProcesor{ + ObjectName: p.ObjectName, + seenObjects: make(map[string]struct{}), + } + for { + reqPageParams := p.BaseParams + reqPageParams.Query.Set("offset", strconv.Itoa(len(paginateRespProcesor.Objects))) + reqPageParams.Query.Set("limit", strconv.Itoa(maxPageLimit)) + reqPageParams.RespProcessor = paginateRespProcesor.Process + + err := p.DoFn(ctx, reqPageParams) + if err != nil { + if errors.Is(err, errEndPagination) { + break + } + return fmt.Errorf("failed to retrieve page: %w", err) + } + if p.Limit > 0 && len(paginateRespProcesor.Objects) >= p.Limit { + break + } + } + + returnCount := len(paginateRespProcesor.Objects) + if p.Limit > 0 && returnCount > p.Limit { + returnCount = p.Limit + } + respBytes, err := json.Marshal(map[string]any{ + "total_count": paginateRespProcesor.TotalCount, + p.ObjectName: paginateRespProcesor.Objects[:returnCount], + }) + if err != nil { + return cli.Exit("Failed to marshal response: "+err.Error(), 1) + } + resp := &http.Response{ + StatusCode: http.StatusOK, + Body: io.NopCloser(bytes.NewReader(respBytes)), + } + return c.defaultRespProcessor(resp) +} + +type paginateRespProcesor struct { + TotalCount int + Objects []any + ObjectName string + seenObjects map[string]struct{} +} + +func (p *paginateRespProcesor) Process(resp *http.Response) error { + if resp.StatusCode != http.StatusOK { + return cli.Exit("Unexpected response status: "+resp.Status, 1) + } + + var pageBody map[string]json.RawMessage + if err := json.NewDecoder(resp.Body).Decode(&pageBody); err != nil { + return cli.Exit("Failed to parse response: "+err.Error(), 1) + } + + if err := json.Unmarshal(pageBody["total_count"], &p.TotalCount); err != nil { + return cli.Exit("Failed to parse total_count: "+err.Error(), 1) + } + + var objects []json.RawMessage + if err := json.Unmarshal(pageBody[p.ObjectName], &objects); err != nil { + return cli.Exit("Failed to parse "+p.ObjectName+": "+err.Error(), 1) + } + + if len(objects) == 0 { + return errEndPagination + } + + for _, obj := range objects { + var objMap map[string]any + if err := json.Unmarshal(obj, &objMap); err != nil { + return cli.Exit("Failed to parse object: "+err.Error(), 1) + } + + id, ok := objMap["id"].(string) + if !ok || id == "" { + return cli.Exit("Object ID is missing or not a string", 1) + } + + if _, seen := p.seenObjects[id]; !seen { + p.seenObjects[id] = struct{}{} + p.Objects = append(p.Objects, objMap) + } + } + return nil +} diff --git a/internal/app/enaptercli/cmd_device_list.go b/internal/app/enaptercli/cmd_device_list.go index 2b90284..2923c1e 100644 --- a/internal/app/enaptercli/cmd_device_list.go +++ b/internal/app/enaptercli/cmd_device_list.go @@ -13,6 +13,7 @@ type cmdDevicesList struct { cmdDevices siteID string expand []string + limit int } func buildCmdDevicesList() *cli.Command { @@ -42,6 +43,11 @@ func (c *cmdDevicesList) Flags() []cli.Flag { Name: "site-id", Usage: "list devices from this site", Destination: &c.siteID, + }, &cli.IntFlag{ + Name: "limit", + Usage: "maximum number of devices to retrieve", + Destination: &c.limit, + DefaultText: "retrieves all", }) } @@ -58,18 +64,22 @@ func (c *cmdDevicesList) do(ctx context.Context) error { query.Set("expand", strings.Join(c.expand, ",")) } - if c.siteID != "" { - query.Set("site_id", c.siteID) - return c.cmdBase.doHTTPRequest(ctx, doHTTPRequestParams{ + doPaginateRequestParams := paginateHTTPRequestParams{ + ObjectName: "devices", + Limit: c.limit, + DoFn: c.doHTTPRequest, + BaseParams: doHTTPRequestParams{ Method: http.MethodGet, - Path: "/sites/" + c.siteID + "/devices", + Path: "", Query: query, - }) + }, } - return c.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodGet, - Path: "", - Query: query, - }) + if c.siteID != "" { + doPaginateRequestParams.BaseParams.Query.Set("site_id", c.siteID) + doPaginateRequestParams.BaseParams.Path = "/sites/" + c.siteID + "/devices" + doPaginateRequestParams.DoFn = c.cmdBase.doHTTPRequest + } + + return c.doPaginateRequest(ctx, doPaginateRequestParams) } diff --git a/internal/app/enaptercli/cmd_site_list.go b/internal/app/enaptercli/cmd_site_list.go index a1d56c8..ec58938 100644 --- a/internal/app/enaptercli/cmd_site_list.go +++ b/internal/app/enaptercli/cmd_site_list.go @@ -10,6 +10,7 @@ import ( type cmdSitesList struct { cmdSite mySites bool + limit int } func buildCmdSitesList() *cli.Command { @@ -32,16 +33,28 @@ func (c *cmdSitesList) Flags() []cli.Flag { Name: "my-sites", Usage: "returns only sites where user is owner or installer", Destination: &c.mySites, + }, &cli.IntFlag{ + Name: "limit", + Usage: "maximum number of sites to retrieve", + Destination: &c.limit, + DefaultText: "retrieves all", }) } func (c *cmdSitesList) do(ctx context.Context) error { - if c.mySites { - return c.cmdBase.doHTTPRequest(ctx, doHTTPRequestParams{ + doPaginateRequestParams := paginateHTTPRequestParams{ + ObjectName: "sites", + Limit: c.limit, + DoFn: c.doHTTPRequest, + BaseParams: doHTTPRequestParams{ Method: http.MethodGet, - Path: "/users/me/sites", - }) + Path: "", + }, + } + + if c.mySites { + doPaginateRequestParams.BaseParams.Path = "/users/me/sites" } - return c.cmdSite.doHTTPRequest(ctx, doHTTPRequestParams{Method: http.MethodGet}) + return c.doPaginateRequest(ctx, doPaginateRequestParams) } diff --git a/internal/app/enaptercli/testdata/helps/enapter device list b/internal/app/enaptercli/testdata/helps/enapter device list index cbd161a..1dd0c0e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device list +++ b/internal/app/enaptercli/testdata/helps/enapter device list @@ -10,6 +10,7 @@ OPTIONS: --verbose log extra details about the operation (default: false) --expand value [ --expand value ] coma-separated list of expanded device information (supported values: connectivity, manifest, properties, communication_info, site) --site-id value list devices from this site + --limit value maximum number of devices to retrieve (default: retrieves all) --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter site list b/internal/app/enaptercli/testdata/helps/enapter site list index 50944fc..e95d0fe 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site list +++ b/internal/app/enaptercli/testdata/helps/enapter site list @@ -9,6 +9,7 @@ OPTIONS: --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --my-sites returns only sites where user is owner or installer (default: false) + --limit value maximum number of sites to retrieve (default: retrieves all) --help, -h show help ENVIRONMENT VARIABLES: From cce32db354395c51186986545d5141bac716bf1a Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 4 Aug 2025 15:15:37 +0300 Subject: [PATCH 55/88] feat: rename provisioning to 'device create' --- internal/app/enaptercli/cmd_device.go | 1 + ...md_provisioning.go => cmd_device_create.go} | 12 ++++++------ ...vice.go => cmd_device_create_lua_device.go} | 18 +++++++++--------- ...lone.go => cmd_device_create_standalone.go} | 14 +++++++------- internal/app/enaptercli/execute.go | 1 - internal/app/enaptercli/testdata/helps/enapter | 11 +++++------ .../enaptercli/testdata/helps/enapter device | 1 + ...pter provisioning => enapter device create} | 4 ++-- ...device => enapter device create lua-device} | 4 ++-- ...dalone => enapter device create standalone} | 4 ++-- 10 files changed, 35 insertions(+), 35 deletions(-) rename internal/app/enaptercli/{cmd_provisioning.go => cmd_device_create.go} (54%) rename internal/app/enaptercli/{cmd_provisioning_lua_device.go => cmd_device_create_lua_device.go} (85%) rename internal/app/enaptercli/{cmd_provisioning_standalone.go => cmd_device_create_standalone.go} (79%) rename internal/app/enaptercli/testdata/helps/{enapter provisioning => enapter device create} (78%) rename internal/app/enaptercli/testdata/helps/{enapter provisioning lua-device => enapter device create lua-device} (88%) rename internal/app/enaptercli/testdata/helps/{enapter provisioning standalone => enapter device create standalone} (85%) diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 6e46b23..3b3b220 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -19,6 +19,7 @@ func buildCmdDevices() *cli.Command { Usage: "Manage devices", CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ + buildCmdDeviceCreate(), buildCmdDevicesList(), buildCmdDevicesGet(), buildCmdDevicesAssignBlueprint(), diff --git a/internal/app/enaptercli/cmd_provisioning.go b/internal/app/enaptercli/cmd_device_create.go similarity index 54% rename from internal/app/enaptercli/cmd_provisioning.go rename to internal/app/enaptercli/cmd_device_create.go index d5a7ea6..1154c1a 100644 --- a/internal/app/enaptercli/cmd_provisioning.go +++ b/internal/app/enaptercli/cmd_device_create.go @@ -4,19 +4,19 @@ import ( "github.com/urfave/cli/v2" ) -type cmdProvisioning struct { +type cmdDeviceCreate struct { cmdBase } -func buildCmdProvisioning() *cli.Command { - cmd := &cmdProvisioning{} +func buildCmdDeviceCreate() *cli.Command { + cmd := &cmdDeviceCreate{} return &cli.Command{ - Name: "provisioning", + Name: "create", Usage: "Create devices of different types", CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ - buildCmdProvisioningStandalone(), - buildCmdProvisioningLua(), + buildCmdDeviceCreateStandalone(), + buildCmdDeviceCreateLua(), }, } } diff --git a/internal/app/enaptercli/cmd_provisioning_lua_device.go b/internal/app/enaptercli/cmd_device_create_lua_device.go similarity index 85% rename from internal/app/enaptercli/cmd_provisioning_lua_device.go rename to internal/app/enaptercli/cmd_device_create_lua_device.go index 443b1f0..a5d3302 100644 --- a/internal/app/enaptercli/cmd_provisioning_lua_device.go +++ b/internal/app/enaptercli/cmd_device_create_lua_device.go @@ -10,8 +10,8 @@ import ( "github.com/urfave/cli/v2" ) -type cmdProvisioningLua struct { - cmdProvisioning +type cmdDeviceCreateLua struct { + cmdDeviceCreate deviceName string deviceSlug string runtimeID string @@ -19,8 +19,8 @@ type cmdProvisioningLua struct { blueprintPath string } -func buildCmdProvisioningLua() *cli.Command { - cmd := &cmdProvisioningLua{} +func buildCmdDeviceCreateLua() *cli.Command { + cmd := &cmdDeviceCreateLua{} return &cli.Command{ Name: "lua-device", Usage: "Create a new Lua device", @@ -33,8 +33,8 @@ func buildCmdProvisioningLua() *cli.Command { } } -func (c *cmdProvisioningLua) Flags() []cli.Flag { - flags := c.cmdProvisioning.Flags() +func (c *cmdDeviceCreateLua) Flags() []cli.Flag { + flags := c.cmdDeviceCreate.Flags() return append(flags, &cli.StringFlag{ Name: "runtime-id", Aliases: []string{"r"}, @@ -63,8 +63,8 @@ func (c *cmdProvisioningLua) Flags() []cli.Flag { }) } -func (c *cmdProvisioningLua) Before(cliCtx *cli.Context) error { - if err := c.cmdProvisioning.Before(cliCtx); err != nil { +func (c *cmdDeviceCreateLua) Before(cliCtx *cli.Context) error { + if err := c.cmdDeviceCreate.Before(cliCtx); err != nil { return err } if c.blueprintID != "" && c.blueprintPath != "" { @@ -76,7 +76,7 @@ func (c *cmdProvisioningLua) Before(cliCtx *cli.Context) error { return nil } -func (c *cmdProvisioningLua) do(ctx context.Context) error { +func (c *cmdDeviceCreateLua) do(ctx context.Context) error { if c.blueprintPath != "" { blueprintID, err := uploadBlueprintAndReturnBlueprintID(ctx, c.blueprintPath, c.cmdBase.doHTTPRequest) if err != nil { diff --git a/internal/app/enaptercli/cmd_provisioning_standalone.go b/internal/app/enaptercli/cmd_device_create_standalone.go similarity index 79% rename from internal/app/enaptercli/cmd_provisioning_standalone.go rename to internal/app/enaptercli/cmd_device_create_standalone.go index b295221..7230eb6 100644 --- a/internal/app/enaptercli/cmd_provisioning_standalone.go +++ b/internal/app/enaptercli/cmd_device_create_standalone.go @@ -10,14 +10,14 @@ import ( "github.com/urfave/cli/v2" ) -type cmdProvisioningStandalone struct { - cmdProvisioning +type cmdDeviceCreateStandalone struct { + cmdDeviceCreate siteID string deviceName string } -func buildCmdProvisioningStandalone() *cli.Command { - cmd := &cmdProvisioningStandalone{} +func buildCmdDeviceCreateStandalone() *cli.Command { + cmd := &cmdDeviceCreateStandalone{} return &cli.Command{ Name: "standalone", Usage: "Create a new standalone device", @@ -30,8 +30,8 @@ func buildCmdProvisioningStandalone() *cli.Command { } } -func (c *cmdProvisioningStandalone) Flags() []cli.Flag { - flags := c.cmdProvisioning.Flags() +func (c *cmdDeviceCreateStandalone) Flags() []cli.Flag { + flags := c.cmdDeviceCreate.Flags() return append(flags, &cli.StringFlag{ Name: "site-id", Aliases: []string{"s"}, @@ -46,7 +46,7 @@ func (c *cmdProvisioningStandalone) Flags() []cli.Flag { }) } -func (c *cmdProvisioningStandalone) do(ctx context.Context) error { +func (c *cmdDeviceCreateStandalone) do(ctx context.Context) error { body, err := json.Marshal(map[string]any{ "site_id": c.siteID, "name": c.deviceName, diff --git a/internal/app/enaptercli/execute.go b/internal/app/enaptercli/execute.go index 89f76e4..c8cdefd 100644 --- a/internal/app/enaptercli/execute.go +++ b/internal/app/enaptercli/execute.go @@ -25,7 +25,6 @@ func NewApp() *cli.App { buildCmdSites(), buildCmdDevices(), buildCmdBlueprints(), - buildCmdProvisioning(), buildCmdRuleEngine(), } diff --git a/internal/app/enaptercli/testdata/helps/enapter b/internal/app/enaptercli/testdata/helps/enapter index 8f11668..9f058d5 100644 --- a/internal/app/enaptercli/testdata/helps/enapter +++ b/internal/app/enaptercli/testdata/helps/enapter @@ -8,12 +8,11 @@ DESCRIPTION: The Enapter CLI requires an access token for authentication. You can obtain the token in your Enapter Cloud account settings. COMMANDS: - site Manage sites - device Manage devices - blueprint Manage blueprints - provisioning Create devices of different types - rule-engine Manage the rule engine - help, h Shows a list of commands or help for one command + site Manage sites + device Manage devices + blueprint Manage blueprints + rule-engine Manage the rule engine + help, h Shows a list of commands or help for one command GLOBAL OPTIONS: --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index 52bc356..ab80274 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -5,6 +5,7 @@ USAGE: enaptercli.test device command [command options] COMMANDS: + create Create devices of different types list List user devices get Retrieve device information assign-blueprint Assign blueprint to device diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning b/internal/app/enaptercli/testdata/helps/enapter device create similarity index 78% rename from internal/app/enaptercli/testdata/helps/enapter provisioning rename to internal/app/enaptercli/testdata/helps/enapter device create index 4ae26f9..fe5efa7 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning +++ b/internal/app/enaptercli/testdata/helps/enapter device create @@ -1,8 +1,8 @@ NAME: - enaptercli.test provisioning - Create devices of different types + enaptercli.test device create - Create devices of different types USAGE: - enaptercli.test provisioning command [command options] + enaptercli.test device create command [command options] COMMANDS: standalone Create a new standalone device diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device b/internal/app/enaptercli/testdata/helps/enapter device create lua-device similarity index 88% rename from internal/app/enaptercli/testdata/helps/enapter provisioning lua-device rename to internal/app/enaptercli/testdata/helps/enapter device create lua-device index c9c95b3..6c1046a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning lua-device +++ b/internal/app/enaptercli/testdata/helps/enapter device create lua-device @@ -1,8 +1,8 @@ NAME: - enaptercli.test provisioning lua-device - Create a new Lua device + enaptercli.test device create lua-device - Create a new Lua device USAGE: - enaptercli.test provisioning lua-device [command options] + enaptercli.test device create lua-device [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] diff --git a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone b/internal/app/enaptercli/testdata/helps/enapter device create standalone similarity index 85% rename from internal/app/enaptercli/testdata/helps/enapter provisioning standalone rename to internal/app/enaptercli/testdata/helps/enapter device create standalone index 76e9215..75e0c76 100644 --- a/internal/app/enaptercli/testdata/helps/enapter provisioning standalone +++ b/internal/app/enaptercli/testdata/helps/enapter device create standalone @@ -1,8 +1,8 @@ NAME: - enaptercli.test provisioning standalone - Create a new standalone device + enaptercli.test device create standalone - Create a new standalone device USAGE: - enaptercli.test provisioning standalone [command options] + enaptercli.test device create standalone [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] From 93040a798efd9d004e92081509a86ada03be505a Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Mon, 4 Aug 2025 16:36:47 +0300 Subject: [PATCH 56/88] feat: rename assign-blueprint to change-blueprint --- internal/app/enaptercli/cmd_device.go | 2 +- ...rint.go => cmd_device_change_blueprint.go} | 20 +++++++++---------- .../cmd_device_create_lua_device.go | 4 ++-- .../enaptercli/testdata/helps/enapter device | 2 +- ...eprint => enapter device change-blueprint} | 8 ++++---- .../helps/enapter device create lua-device | 4 ++-- .../device_assign_blueprint_by_id/cmd.tmpl | 2 +- .../cmd.tmpl | 2 +- .../device_assign_blueprint_by_path/cmd.tmpl | 2 +- .../device_assign_blueprint_by_zip/cmd.tmpl | 2 +- .../cmd.tmpl | 2 +- 11 files changed, 25 insertions(+), 25 deletions(-) rename internal/app/enaptercli/{cmd_device_assign_blueprint.go => cmd_device_change_blueprint.go} (75%) rename internal/app/enaptercli/testdata/helps/{enapter device assign-blueprint => enapter device change-blueprint} (76%) diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 3b3b220..e386607 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -22,7 +22,7 @@ func buildCmdDevices() *cli.Command { buildCmdDeviceCreate(), buildCmdDevicesList(), buildCmdDevicesGet(), - buildCmdDevicesAssignBlueprint(), + buildCmdDevicesChangeBlueprint(), buildCmdDevicesLogs(), buildCmdDevicesDelete(), buildCmdDeviceCommand(), diff --git a/internal/app/enaptercli/cmd_device_assign_blueprint.go b/internal/app/enaptercli/cmd_device_change_blueprint.go similarity index 75% rename from internal/app/enaptercli/cmd_device_assign_blueprint.go rename to internal/app/enaptercli/cmd_device_change_blueprint.go index 740280f..38ec7c4 100644 --- a/internal/app/enaptercli/cmd_device_assign_blueprint.go +++ b/internal/app/enaptercli/cmd_device_change_blueprint.go @@ -10,18 +10,18 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDevicesAssignBlueprint struct { +type cmdDevicesChangeBlueprint struct { cmdDevices deviceID string blueprintID string blueprintPath string } -func buildCmdDevicesAssignBlueprint() *cli.Command { - cmd := &cmdDevicesAssignBlueprint{} +func buildCmdDevicesChangeBlueprint() *cli.Command { + cmd := &cmdDevicesChangeBlueprint{} return &cli.Command{ - Name: "assign-blueprint", - Usage: "Assign blueprint to device", + Name: "change-blueprint", + Usage: "Change blueprint to device", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, @@ -31,7 +31,7 @@ func buildCmdDevicesAssignBlueprint() *cli.Command { } } -func (c *cmdDevicesAssignBlueprint) Flags() []cli.Flag { +func (c *cmdDevicesChangeBlueprint) Flags() []cli.Flag { flags := c.cmdDevices.Flags() return append(flags, &cli.StringFlag{ Name: "device-id", @@ -42,16 +42,16 @@ func (c *cmdDevicesAssignBlueprint) Flags() []cli.Flag { }, &cli.StringFlag{ Name: "blueprint-id", Aliases: []string{"b"}, - Usage: "blueprint ID to assign", + Usage: "blueprint ID to use as new device blueprint", Destination: &c.blueprintID, }, &cli.StringFlag{ Name: "blueprint-path", - Usage: "blueprint path (zip file or directory) to assign", + Usage: "blueprint path (zip file or directory) to use as new device blueprint", Destination: &c.blueprintPath, }) } -func (c *cmdDevicesAssignBlueprint) Before(cliCtx *cli.Context) error { +func (c *cmdDevicesChangeBlueprint) Before(cliCtx *cli.Context) error { if err := c.cmdDevices.Before(cliCtx); err != nil { return err } @@ -64,7 +64,7 @@ func (c *cmdDevicesAssignBlueprint) Before(cliCtx *cli.Context) error { return c.validateExpandFlag(cliCtx) } -func (c *cmdDevicesAssignBlueprint) do(ctx context.Context) error { +func (c *cmdDevicesChangeBlueprint) do(ctx context.Context) error { if c.blueprintPath != "" { blueprintID, err := uploadBlueprintAndReturnBlueprintID(ctx, c.blueprintPath, c.cmdBase.doHTTPRequest) if err != nil { diff --git a/internal/app/enaptercli/cmd_device_create_lua_device.go b/internal/app/enaptercli/cmd_device_create_lua_device.go index a5d3302..a72ad67 100644 --- a/internal/app/enaptercli/cmd_device_create_lua_device.go +++ b/internal/app/enaptercli/cmd_device_create_lua_device.go @@ -54,11 +54,11 @@ func (c *cmdDeviceCreateLua) Flags() []cli.Flag { }, &cli.StringFlag{ Name: "blueprint-id", Aliases: []string{"b"}, - Usage: "blueprint ID for the new Lua device", + Usage: "blueprint ID to use for the new Lua device", Destination: &c.blueprintID, }, &cli.StringFlag{ Name: "blueprint-path", - Usage: "blueprint path (zip file or directory) to assign", + Usage: "blueprint path (zip file or directory) to use for the new Lua device", Destination: &c.blueprintPath, }) } diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index ab80274..6ebdb95 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -8,7 +8,7 @@ COMMANDS: create Create devices of different types list List user devices get Retrieve device information - assign-blueprint Assign blueprint to device + change-blueprint Change blueprint to device logs Show device logs delete Delete a device command Manage device commands diff --git a/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint b/internal/app/enaptercli/testdata/helps/enapter device change-blueprint similarity index 76% rename from internal/app/enaptercli/testdata/helps/enapter device assign-blueprint rename to internal/app/enaptercli/testdata/helps/enapter device change-blueprint index c191e45..0189840 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device assign-blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter device change-blueprint @@ -1,16 +1,16 @@ NAME: - enaptercli.test device assign-blueprint - Assign blueprint to device + enaptercli.test device change-blueprint - Change blueprint to device USAGE: - enaptercli.test device assign-blueprint [command options] + enaptercli.test device change-blueprint [command options] OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --device-id value, -d value device ID - --blueprint-id value, -b value blueprint ID to assign - --blueprint-path value blueprint path (zip file or directory) to assign + --blueprint-id value, -b value blueprint ID to use as new device blueprint + --blueprint-path value blueprint path (zip file or directory) to use as new device blueprint --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter device create lua-device b/internal/app/enaptercli/testdata/helps/enapter device create lua-device index 6c1046a..a082ac1 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device create lua-device +++ b/internal/app/enaptercli/testdata/helps/enapter device create lua-device @@ -11,8 +11,8 @@ OPTIONS: --runtime-id value, -r value UCM device ID where the new Lua device will run --device-name value, -n value name for the new Lua device --device-slug value slug for the new Lua device - --blueprint-id value, -b value blueprint ID for the new Lua device - --blueprint-path value blueprint path (zip file or directory) to assign + --blueprint-id value, -b value blueprint ID to use for the new Lua device + --blueprint-path value blueprint path (zip file or directory) to use for the new Lua device --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/cmd.tmpl index ddc8a25..c36d0ca 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/cmd.tmpl +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/cmd.tmpl @@ -1 +1 @@ -enapter3 device assign-blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 +enapter3 device change-blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/cmd.tmpl index ab9cde0..503d2e2 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/cmd.tmpl +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/cmd.tmpl @@ -1 +1 @@ -enapter3 device assign-blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 --blueprint-path ./testdata/blueprints/bp.zip +enapter3 device change-blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 --blueprint-path ./testdata/blueprints/bp.zip diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/cmd.tmpl index 33e057f..10c4e95 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/cmd.tmpl +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/cmd.tmpl @@ -1 +1 @@ -enapter3 device assign-blueprint {{.BaseFlags}} --device-id 3b0a0626-2dc4-44a3-ac5a-34d58b7b2a26 --blueprint-path ./testdata/blueprints/simple +enapter3 device change-blueprint {{.BaseFlags}} --device-id 3b0a0626-2dc4-44a3-ac5a-34d58b7b2a26 --blueprint-path ./testdata/blueprints/simple diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/cmd.tmpl index ab232f3..abb0189 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/cmd.tmpl +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/cmd.tmpl @@ -1 +1 @@ -enapter3 device assign-blueprint {{.BaseFlags}} --device-id 3b0a0626-2dc4-44a3-ac5a-34d58b7b2a26 --blueprint-path ./testdata/blueprints/bp.zip +enapter3 device change-blueprint {{.BaseFlags}} --device-id 3b0a0626-2dc4-44a3-ac5a-34d58b7b2a26 --blueprint-path ./testdata/blueprints/bp.zip diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/cmd.tmpl index bace8f1..c658f5c 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/cmd.tmpl +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/cmd.tmpl @@ -1 +1 @@ -enapter3 device assign-blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 +enapter3 device change-blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 From 51a58cc8e474b9171f00b215a665931b82fd4a7e Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 22 Aug 2025 17:09:53 +0300 Subject: [PATCH 57/88] bug: device generate communication config should be POST --- .../cmd_device_communication_config_generate.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/internal/app/enaptercli/cmd_device_communication_config_generate.go b/internal/app/enaptercli/cmd_device_communication_config_generate.go index 1e3f821..8853668 100644 --- a/internal/app/enaptercli/cmd_device_communication_config_generate.go +++ b/internal/app/enaptercli/cmd_device_communication_config_generate.go @@ -41,13 +41,6 @@ func (c *cmdDeviceCommunicationConfigGenerate) Flags() []cli.Flag { ) } -// func (c *cmdDeviceCommandGet) Before(cliCtx *cli.Context) error { -// if err := c.cmdDevices.Before(cliCtx); err != nil { -// return err -// } -// return validateExpandFlag(cliCtx, []string{"log"}) -// } - func (c *cmdDeviceCommunicationConfigGenerate) do(ctx context.Context) error { reqBody := struct { Protocol string `json:"protocol"` @@ -60,7 +53,7 @@ func (c *cmdDeviceCommunicationConfigGenerate) do(ctx context.Context) error { } return c.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodGet, + Method: http.MethodPost, Path: "/generate_config", Body: bytes.NewReader(data), }) From 0512c02f1354fe25e40927c29a881b1730038470 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 22 Aug 2025 17:14:28 +0300 Subject: [PATCH 58/88] bug: create lua_device or change bluerint should handle blueprin upload errors --- internal/app/enaptercli/cmd_blueprints_upload.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/app/enaptercli/cmd_blueprints_upload.go b/internal/app/enaptercli/cmd_blueprints_upload.go index e3a2e79..fee24fb 100644 --- a/internal/app/enaptercli/cmd_blueprints_upload.go +++ b/internal/app/enaptercli/cmd_blueprints_upload.go @@ -51,6 +51,10 @@ func uploadBlueprintAndReturnBlueprintID(ctx context.Context, blueprintPath stri var blueprintID string err := uploadBlueprint(ctx, blueprintPath, func(ctx context.Context, reqParams doHTTPRequestParams) error { reqParams.RespProcessor = func(resp *http.Response) error { + if resp.StatusCode != http.StatusOK { + return cli.Exit(parseRespErrorMessage(resp), 1) + } + var respBlueprint struct { Blueprint struct { ID string `json:"id"` From ce7f74242235f9ea1faa40e29fe5c9f652933925 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Fri, 22 Aug 2025 18:37:20 +0300 Subject: [PATCH 59/88] doc: improve usage for duration flags --- internal/app/enaptercli/cmd_rule_engine_rule_create.go | 5 +++-- .../app/enaptercli/cmd_rule_engine_rule_update_script.go | 5 +++-- .../testdata/helps/enapter rule-engine rule create | 2 +- .../testdata/helps/enapter rule-engine rule update-script | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_create.go b/internal/app/enaptercli/cmd_rule_engine_rule_create.go index fa23d3b..d865b06 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_create.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_create.go @@ -63,8 +63,9 @@ func (c *cmdRuleEngineRuleCreate) Flags() []cli.Flag { }, &cliflags.Duration{ DurationFlag: cli.DurationFlag{ - Name: "exec-interval", - Usage: "How often to execute the script (only compatible with the runtime version 1)", + Name: "exec-interval", + Usage: "How frequently to execute the script " + + "(compatible only with runtime version 1) in duration format (e.g., 5s, 2m)", Destination: &c.execInterval, }, }, diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go index 8d04311..a1e6f55 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go @@ -62,8 +62,9 @@ func (c *cmdRuleEngineRuleUpdateScript) Flags() []cli.Flag { }, &cliflags.Duration{ DurationFlag: cli.DurationFlag{ - Name: "exec-interval", - Usage: "How often to execute the script (only compatible with the runtime version 1)", + Name: "exec-interval", + Usage: "How frequently to execute the script " + + "(compatible only with runtime version 1) in duration format (e.g., 5s, 2m)", Destination: &c.execInterval, }, }, diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create index c7ab5eb..b2ab0c1 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create @@ -11,7 +11,7 @@ OPTIONS: --slug value Slug for the new rule --script value Path to the file containing the script code --runtime-version value Version of the runtime to use for the script execution (default: "V3") - --exec-interval value How often to execute the script (only compatible with the runtime version 1) + --exec-interval value How frequently to execute the script (compatible only with runtime version 1) in duration format (e.g., 5s, 2m) --disable Disable the rule upon creation (default: false) --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script index 89c2a9a..c5355c0 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script @@ -11,7 +11,7 @@ OPTIONS: --rule-id value Rule ID or slug to update --script value Path to a file containing the script code --runtime-version value Version of the runtime to use for the script execution (default: "V3") - --exec-interval value How often to execute the script (only compatible with the runtime version 1) + --exec-interval value How frequently to execute the script (compatible only with runtime version 1) in duration format (e.g., 5s, 2m) --help, -h show help ENVIRONMENT VARIABLES: From 8d794fa9c71cc226abb48fb7f8a2d53a99b41d9d Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Mon, 25 Aug 2025 12:28:35 +0400 Subject: [PATCH 60/88] feat: support TLS for WebSockets --- internal/app/enaptercli/cmd_base.go | 73 +++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 82c8432..165a86c 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -205,35 +205,44 @@ func (c *cmdBase) dialWebSocket( } url.RawQuery = query.Encode() - switch url.Scheme { - case "https": - url.Scheme = "wss" - case "http": - url.Scheme = "ws" - } - headers := make(http.Header) headers.Add("X-Enapter-Auth-Token", c.token) - if c.verbose { - fmt.Fprintf(c.writer, "== Dialing WebSocket at %s\n", url.String()) - } - const timeout = 5 * time.Second dialer := websocket.Dialer{ HandshakeTimeout: timeout, + //nolint:gosec // This is needed to allow self-signed certificates on Gateway. + TLSClientConfig: &tls.Config{InsecureSkipVerify: c.apiAllowInsecure}, } - //nolint:bodyclose // body should be closed by callers - conn, resp, err := dialer.DialContext(ctx, url.String(), headers) - if err != nil { - return nil, fmt.Errorf("dial: %w", err) - } - if resp.StatusCode != http.StatusSwitchingProtocols { - return nil, cli.Exit(parseRespErrorMessage(resp), 1) + const maxRetries = 2 + for i := 0; i < maxRetries; i++ { + url.Scheme = websocketScheme(url.Scheme) + + if c.verbose { + fmt.Fprintf(c.writer, "== Dialing WebSocket at %s\n", url.String()) + } + + //nolint:bodyclose // body should be closed by callers + conn, resp, err := dialer.DialContext(ctx, url.String(), headers) + if err != nil { + if loc, err := redirectLocation(resp); err != nil { + return nil, err + } else if loc != nil { + url = loc + continue + } + return nil, fmt.Errorf("dial: %w", err) + } + + if resp.StatusCode != http.StatusSwitchingProtocols { + return nil, cli.Exit(parseRespErrorMessage(resp), 1) + } + + return conn, nil } - return conn, nil + return nil, cli.Exit("Too many redirects", 1) } func (c *cmdBase) readWebSocket( @@ -320,3 +329,29 @@ func validateFlag(context, value string, allowedValues []string) error { } return nil } + +func websocketScheme(s string) string { + switch s { + case "https": + return "wss" + case "http": + return "ws" + default: + return s + } +} + +func redirectLocation(resp *http.Response) (*url.URL, error) { + if resp == nil { + return nil, nil + } + if resp.StatusCode != http.StatusPermanentRedirect { + return nil, nil + } + location := resp.Header.Get("Location") + url, err := url.Parse(location) + if err != nil { + return nil, fmt.Errorf("parse location: %w", err) + } + return url, nil +} From 12daafe544238281984bb138bba107868d1827bf Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Thu, 28 Aug 2025 12:45:48 +0300 Subject: [PATCH 61/88] feat: delete rule one by one --- .../enaptercli/cmd_rule_engine_rule_delete.go | 30 ++++++------------- .../testdata/helps/enapter rule-engine rule | 2 +- .../helps/enapter rule-engine rule delete | 12 ++++---- 3 files changed, 16 insertions(+), 28 deletions(-) diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_delete.go b/internal/app/enaptercli/cmd_rule_engine_rule_delete.go index 9e92c54..ef82b7d 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_delete.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_delete.go @@ -1,10 +1,7 @@ package enaptercli import ( - "bytes" "context" - "encoding/json" - "fmt" "net/http" "github.com/urfave/cli/v2" @@ -12,14 +9,14 @@ import ( type cmdRuleEngineRuleDelete struct { cmdRuleEngineRule - ruleIDs []string + ruleID string } func buildCmdRuleEngineRuleDelete() *cli.Command { cmd := &cmdRuleEngineRuleDelete{} return &cli.Command{ Name: "delete", - Usage: "Delete one or more rules", + Usage: "Delete a rule", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, @@ -31,28 +28,19 @@ func buildCmdRuleEngineRuleDelete() *cli.Command { func (c *cmdRuleEngineRuleDelete) Flags() []cli.Flag { return append(c.cmdRuleEngineRule.Flags(), - &cli.MultiStringFlag{ - Target: &cli.StringSliceFlag{ - Name: "rule-id", - Usage: "Rule IDs or slugs", - Required: true, - }, - Destination: &c.ruleIDs, + &cli.StringFlag{ + Name: "rule-id", + Usage: "Rule ID or slug", + Required: true, + Destination: &c.ruleID, }, ) } func (c *cmdRuleEngineRuleDelete) do(ctx context.Context) error { - body, err := json.Marshal(map[string]any{ - "rule_ids": c.ruleIDs, - }) - if err != nil { - return fmt.Errorf("build request: %w", err) - } return c.doHTTPRequest(ctx, doHTTPRequestParams{ - Method: http.MethodPost, - Path: "/batch_delete", - Body: bytes.NewReader(body), + Method: http.MethodDelete, + Path: "/" + c.ruleID, ContentType: contentTypeJSON, }) } diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule index 462f57c..6996d20 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule @@ -6,7 +6,7 @@ USAGE: COMMANDS: create Create a new rule - delete Delete one or more rules + delete Delete a rule disable Disable one or more rules enable Enable one or more rules get Retrieve a rule diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete index b25fcab..f7dfcc3 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete @@ -1,15 +1,15 @@ NAME: - enaptercli.test rule-engine rule delete - Delete one or more rules + enaptercli.test rule-engine rule delete - Delete a rule USAGE: enaptercli.test rule-engine rule delete [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --rule-id value [ --rule-id value ] Rule IDs or slugs - --help, -h show help + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --rule-id value Rule ID or slug + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token From 04ad25c8165e8c1fc2c6e5494bc59844a3c647ee Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Thu, 28 Aug 2025 13:16:54 +0300 Subject: [PATCH 62/88] chore: update ci --- .goreleaser.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index 3706b10..a12d9cb 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -27,11 +27,11 @@ release: name: enapter-cli archives: - - format: tar.gz + - formats: ['tar.gz'] wrap_in_directory: true format_overrides: - goos: windows - format: zip + formats: ['zip'] name_template: '{{ .ProjectName }}-{{ .Version }}-{{ .Os }}-{{ .Arch }}' checksum: From 6899d6cd53f5910c8bb6a6300cd52a525c76c67d Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Thu, 28 Aug 2025 17:08:06 +0400 Subject: [PATCH 63/88] feat: write CLI logs to stderr --- internal/app/enaptercli/cmd_base.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index 165a86c..f989408 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -25,6 +25,7 @@ type cmdBase struct { apiHost string apiAllowInsecure bool writer io.Writer + errWriter io.Writer httpClient *http.Client } @@ -67,6 +68,7 @@ func (c *cmdBase) Before(cliCtx *cli.Context) error { return errAPITokenMissed } c.writer = cliCtx.App.Writer + c.errWriter = cliCtx.App.ErrWriter c.httpClient = &http.Client{ Transport: &http.Transport{ //nolint:gosec // This is needed to allow self-signed certificates on Gateway. @@ -118,8 +120,8 @@ func (c *cmdBase) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) erro return err } - fmt.Fprintf(c.writer, "== Do http request %s %s\n", p.Method, req.URL.String()) - fmt.Fprintf(c.writer, "=== Begin body\n%s\n=== End body\n", bodyStr) + fmt.Fprintf(c.errWriter, "== Do http request %s %s\n", p.Method, req.URL.String()) + fmt.Fprintf(c.errWriter, "=== Begin body\n%s\n=== End body\n", bodyStr) } resp, err := c.httpClient.Do(req) @@ -146,7 +148,7 @@ type runWebSocketParams struct { func (c *cmdBase) runWebSocket(ctx context.Context, p runWebSocketParams) error { for retry := false; ; retry = true { if retry { - fmt.Fprintln(c.writer, "Reconnecting...") + fmt.Fprintln(c.errWriter, "Reconnecting...") time.Sleep(time.Second) } @@ -156,11 +158,11 @@ func (c *cmdBase) runWebSocket(ctx context.Context, p runWebSocketParams) error case <-ctx.Done(): return nil default: - fmt.Fprintln(c.writer, "Failed to retrieve data:", err) + fmt.Fprintln(c.errWriter, "Failed to retrieve data:", err) continue } } - fmt.Fprintln(c.writer, "Connection established") + fmt.Fprintln(c.errWriter, "Connection established") closeCh := make(chan struct{}) go func() { @@ -176,7 +178,7 @@ func (c *cmdBase) runWebSocket(ctx context.Context, p runWebSocketParams) error case <-ctx.Done(): return nil default: - fmt.Fprintln(c.writer, "Failed to retrieve data:", err) + fmt.Fprintln(c.errWriter, "Failed to retrieve data:", err) close(closeCh) } } @@ -220,7 +222,7 @@ func (c *cmdBase) dialWebSocket( url.Scheme = websocketScheme(url.Scheme) if c.verbose { - fmt.Fprintf(c.writer, "== Dialing WebSocket at %s\n", url.String()) + fmt.Fprintf(c.errWriter, "== Dialing WebSocket at %s\n", url.String()) } //nolint:bodyclose // body should be closed by callers From 3bedad76fe4f48dba23056eab1e2d3ee2cf1de1b Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Thu, 28 Aug 2025 19:05:58 +0400 Subject: [PATCH 64/88] feat: improve WebSockets error handling --- internal/app/enaptercli/cmd_base.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index f989408..d7192c2 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -154,6 +154,9 @@ func (c *cmdBase) runWebSocket(ctx context.Context, p runWebSocketParams) error conn, err := c.dialWebSocket(ctx, p.Path, p.Query) if err != nil { + if e := cli.ExitCoder(nil); errors.As(err, &e) { + return err + } select { case <-ctx.Done(): return nil @@ -234,13 +237,17 @@ func (c *cmdBase) dialWebSocket( url = loc continue } + if e := (&tls.CertificateVerificationError{}); errors.As(err, &e) { + message := fmt.Sprintf("dial: %v (try to use --api-allow-insecure)", err) + return nil, cli.Exit(message, 1) + } + if resp != nil { + message := parseRespErrorMessage(resp) + return nil, fmt.Errorf("dial: %w: %s", err, message) + } return nil, fmt.Errorf("dial: %w", err) } - if resp.StatusCode != http.StatusSwitchingProtocols { - return nil, cli.Exit(parseRespErrorMessage(resp), 1) - } - return conn, nil } From 00ed95cf69e36cf6d19985ba4dff5e20e5d8c692 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Thu, 28 Aug 2025 19:29:05 +0400 Subject: [PATCH 65/88] feat: add device telemetry command --- internal/app/enaptercli/cmd_device.go | 1 + .../app/enaptercli/cmd_device_telemetry.go | 84 +++++++++++++++++++ .../enaptercli/testdata/helps/enapter device | 1 + .../testdata/helps/enapter device telemetry | 19 +++++ 4 files changed, 105 insertions(+) create mode 100644 internal/app/enaptercli/cmd_device_telemetry.go create mode 100644 internal/app/enaptercli/testdata/helps/enapter device telemetry diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index e386607..e6548ed 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -26,6 +26,7 @@ func buildCmdDevices() *cli.Command { buildCmdDevicesLogs(), buildCmdDevicesDelete(), buildCmdDeviceCommand(), + buildCmdDeviceTelemetry(), buildCmdDeviceCommunicationConfig(), }, } diff --git a/internal/app/enaptercli/cmd_device_telemetry.go b/internal/app/enaptercli/cmd_device_telemetry.go new file mode 100644 index 0000000..0ac7db6 --- /dev/null +++ b/internal/app/enaptercli/cmd_device_telemetry.go @@ -0,0 +1,84 @@ +package enaptercli + +import ( + "context" + "fmt" + "io" + "net/http" + "strings" + + "github.com/urfave/cli/v2" +) + +type cmdDeviceTelemetry struct { + cmdDevices + deviceID string + follow bool +} + +func buildCmdDeviceTelemetry() *cli.Command { + cmd := &cmdDeviceTelemetry{} + return &cli.Command{ + Name: "telemetry", + Usage: "Show device telemetry", + CustomHelpTemplate: cmd.CommandHelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdDeviceTelemetry) Flags() []cli.Flag { + flags := c.cmdDevices.Flags() + return append(flags, &cli.StringFlag{ + Name: "device-id", + Aliases: []string{"d"}, + Usage: "device ID", + Destination: &c.deviceID, + Required: true, + }, &cli.BoolFlag{ + Name: "follow", + Aliases: []string{"f"}, + Usage: "follow the telemetry output", + Destination: &c.follow, + }) +} + +func (c *cmdDeviceTelemetry) do(ctx context.Context) error { + if c.follow { + return c.doFollow(ctx) + } + return c.doList(ctx) +} + +func (c *cmdDeviceTelemetry) doFollow(ctx context.Context) error { + return c.runWebSocket(ctx, runWebSocketParams{ + Path: "/devices/" + c.deviceID + "/telemetry", + RespProcessor: func(r io.Reader) error { + payload, err := io.ReadAll(r) + if err != nil { + return fmt.Errorf("read response: %w", err) + } + fmt.Fprintln(c.writer, strings.TrimSpace(string(payload))) + return nil + }, + }) +} + +func (c *cmdDeviceTelemetry) doList(ctx context.Context) error { + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/" + c.deviceID + "/telemetry", + //nolint:bodyclose //body is closed in doHTTPRequest + RespProcessor: okRespBodyProcessor(func(r io.Reader) error { + payload, err := io.ReadAll(r) + if err != nil { + return fmt.Errorf("read response: %w", err) + } + fmt.Fprintln(c.writer, strings.TrimSpace(string(payload))) + return nil + }), + }) +} diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index 6ebdb95..6963cbe 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -12,6 +12,7 @@ COMMANDS: logs Show device logs delete Delete a device command Manage device commands + telemetry Show device telemetry communication-config Manage device communication config help, h Shows a list of commands or help for one command diff --git a/internal/app/enaptercli/testdata/helps/enapter device telemetry b/internal/app/enaptercli/testdata/helps/enapter device telemetry new file mode 100644 index 0000000..7e52736 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter device telemetry @@ -0,0 +1,19 @@ +NAME: + enaptercli.test device telemetry - Show device telemetry + +USAGE: + enaptercli.test device telemetry [command options] + +OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --device-id value, -d value device ID + --follow, -f follow the telemetry output (default: false) + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) + From 995790a130db849981a989e4ab7028a1d48e0d6a Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Wed, 3 Sep 2025 13:35:56 +0300 Subject: [PATCH 66/88] fix: rename new_rule_script to script when update rule --- internal/app/enaptercli/cmd_rule_engine_rule_update_script.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go index a1e6f55..9e1ee24 100644 --- a/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go +++ b/internal/app/enaptercli/cmd_rule_engine_rule_update_script.go @@ -81,7 +81,7 @@ func (c *cmdRuleEngineRuleUpdateScript) do(ctx context.Context) error { } body, err := json.Marshal(map[string]any{ - "new_rule_script": map[string]any{ + "script": map[string]any{ "code": base64.StdEncoding.EncodeToString(scriptBytes), "runtime_version": c.runtimeVersion, "exec_interval": c.execInterval.String(), From 203bd8b5aa33fd2c0b2ea618b2b2a72241a0d690 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Wed, 3 Sep 2025 13:37:51 +0300 Subject: [PATCH 67/88] help: add notice about device list order --- internal/app/enaptercli/cmd_device_list.go | 2 +- internal/app/enaptercli/testdata/helps/enapter device | 2 +- internal/app/enaptercli/testdata/helps/enapter device list | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/app/enaptercli/cmd_device_list.go b/internal/app/enaptercli/cmd_device_list.go index 2923c1e..eefb591 100644 --- a/internal/app/enaptercli/cmd_device_list.go +++ b/internal/app/enaptercli/cmd_device_list.go @@ -20,7 +20,7 @@ func buildCmdDevicesList() *cli.Command { cmd := &cmdDevicesList{} return &cli.Command{ Name: "list", - Usage: "List user devices", + Usage: "List user devices ordered by device ID", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index 6963cbe..eb3fad3 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -6,7 +6,7 @@ USAGE: COMMANDS: create Create devices of different types - list List user devices + list List user devices ordered by device ID get Retrieve device information change-blueprint Change blueprint to device logs Show device logs diff --git a/internal/app/enaptercli/testdata/helps/enapter device list b/internal/app/enaptercli/testdata/helps/enapter device list index 1dd0c0e..1a4d3b1 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device list +++ b/internal/app/enaptercli/testdata/helps/enapter device list @@ -1,5 +1,5 @@ NAME: - enaptercli.test device list - List user devices + enaptercli.test device list - List user devices ordered by device ID USAGE: enaptercli.test device list [command options] From 0e26007e5039d61353b6493140e475b87b9397b6 Mon Sep 17 00:00:00 2001 From: Aleksey Bakin Date: Wed, 3 Sep 2025 13:35:37 +0300 Subject: [PATCH 68/88] fix: update device expand values --- internal/app/enaptercli/cmd_device.go | 7 +++++-- internal/app/enaptercli/cmd_device_get.go | 2 +- internal/app/enaptercli/cmd_device_list.go | 2 +- internal/app/enaptercli/testdata/helps/enapter device get | 2 +- internal/app/enaptercli/testdata/helps/enapter device list | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index e6548ed..79df9b0 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -42,6 +42,9 @@ func (c *cmdDevices) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) e } func (c *cmdDevices) validateExpandFlag(cliCtx *cli.Context) error { - supportedFields := []string{"connectivity", "manifest", "properties", "communication_info", "site"} - return validateExpandFlag(cliCtx, supportedFields) + return validateExpandFlag(cliCtx, c.supportedExpandFields()) +} + +func (c *cmdDevices) supportedExpandFields() []string { + return []string{"connectivity", "manifest", "properties", "communication", "site"} } diff --git a/internal/app/enaptercli/cmd_device_get.go b/internal/app/enaptercli/cmd_device_get.go index 3d754eb..9dc4edd 100644 --- a/internal/app/enaptercli/cmd_device_get.go +++ b/internal/app/enaptercli/cmd_device_get.go @@ -41,7 +41,7 @@ func (c *cmdDevicesGet) Flags() []cli.Flag { Target: &cli.StringSliceFlag{ Name: "expand", Usage: "coma-separated list of expanded device information (supported values: " + - "connectivity, manifest, properties, communication_info, site)", + strings.Join(c.supportedExpandFields(), ", ") + ")", }, Destination: &c.expand, }) diff --git a/internal/app/enaptercli/cmd_device_list.go b/internal/app/enaptercli/cmd_device_list.go index eefb591..92d73e3 100644 --- a/internal/app/enaptercli/cmd_device_list.go +++ b/internal/app/enaptercli/cmd_device_list.go @@ -36,7 +36,7 @@ func (c *cmdDevicesList) Flags() []cli.Flag { Target: &cli.StringSliceFlag{ Name: "expand", Usage: "coma-separated list of expanded device information (supported values: " + - "connectivity, manifest, properties, communication_info, site)", + strings.Join(c.supportedExpandFields(), ", ") + ")", }, Destination: &c.expand, }, &cli.StringFlag{ diff --git a/internal/app/enaptercli/testdata/helps/enapter device get b/internal/app/enaptercli/testdata/helps/enapter device get index 87491ab..0c7a463 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device get +++ b/internal/app/enaptercli/testdata/helps/enapter device get @@ -9,7 +9,7 @@ OPTIONS: --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --device-id value, -d value device ID - --expand value [ --expand value ] coma-separated list of expanded device information (supported values: connectivity, manifest, properties, communication_info, site) + --expand value [ --expand value ] coma-separated list of expanded device information (supported values: connectivity, manifest, properties, communication, site) --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter device list b/internal/app/enaptercli/testdata/helps/enapter device list index 1a4d3b1..b7370ed 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device list +++ b/internal/app/enaptercli/testdata/helps/enapter device list @@ -8,7 +8,7 @@ OPTIONS: --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) - --expand value [ --expand value ] coma-separated list of expanded device information (supported values: connectivity, manifest, properties, communication_info, site) + --expand value [ --expand value ] coma-separated list of expanded device information (supported values: connectivity, manifest, properties, communication, site) --site-id value list devices from this site --limit value maximum number of devices to retrieve (default: retrieves all) --help, -h show help From bdce974e8bc0a627e49f87782dc4f4d6b6359b55 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Mon, 8 Sep 2025 18:39:41 +0400 Subject: [PATCH 69/88] feat: add device update command --- internal/app/enaptercli/cmd_device.go | 1 + internal/app/enaptercli/cmd_device_update.go | 73 +++++++++++++++++++ .../enaptercli/testdata/helps/enapter device | 1 + .../testdata/helps/enapter device update | 20 +++++ 4 files changed, 95 insertions(+) create mode 100644 internal/app/enaptercli/cmd_device_update.go create mode 100644 internal/app/enaptercli/testdata/helps/enapter device update diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 79df9b0..b62bffa 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -24,6 +24,7 @@ func buildCmdDevices() *cli.Command { buildCmdDevicesGet(), buildCmdDevicesChangeBlueprint(), buildCmdDevicesLogs(), + buildCmdDeviceUpdate(), buildCmdDevicesDelete(), buildCmdDeviceCommand(), buildCmdDeviceTelemetry(), diff --git a/internal/app/enaptercli/cmd_device_update.go b/internal/app/enaptercli/cmd_device_update.go new file mode 100644 index 0000000..0ba062e --- /dev/null +++ b/internal/app/enaptercli/cmd_device_update.go @@ -0,0 +1,73 @@ +package enaptercli + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/urfave/cli/v2" +) + +type cmdDeviceUpdate struct { + cmdDevices + deviceID string + name string + slug string +} + +func buildCmdDeviceUpdate() *cli.Command { + cmd := &cmdDeviceUpdate{} + return &cli.Command{ + Name: "update", + Usage: "Update a device", + CustomHelpTemplate: cmd.CommandHelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdDeviceUpdate) Flags() []cli.Flag { + flags := c.cmdDevices.Flags() + return append(flags, + &cli.StringFlag{ + Name: "device-id", + Aliases: []string{"d"}, + Usage: "device ID", + Destination: &c.deviceID, + Required: true, + }, + &cli.StringFlag{ + Name: "name", + Usage: "device name", + Destination: &c.name, + }, + &cli.StringFlag{ + Name: "slug", + Usage: "device slug", + Destination: &c.slug, + }, + ) +} + +func (c *cmdDeviceUpdate) do(ctx context.Context) error { + payload := map[string]string{ + "name": c.name, + "slug": c.slug, + } + body, err := json.Marshal(payload) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + + return c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPatch, + Path: "/" + c.deviceID, + Body: bytes.NewReader(body), + ContentType: contentTypeJSON, + }) +} diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index eb3fad3..7901a83 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -10,6 +10,7 @@ COMMANDS: get Retrieve device information change-blueprint Change blueprint to device logs Show device logs + update Update a device delete Delete a device command Manage device commands telemetry Show device telemetry diff --git a/internal/app/enaptercli/testdata/helps/enapter device update b/internal/app/enaptercli/testdata/helps/enapter device update new file mode 100644 index 0000000..7959280 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter device update @@ -0,0 +1,20 @@ +NAME: + enaptercli.test device update - Update a device + +USAGE: + enaptercli.test device update [command options] + +OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --device-id value, -d value device ID + --name value device name + --slug value device slug + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) + From 7ba2b35a9be19171ac82617fb9c246528d842e16 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Mon, 8 Sep 2025 18:48:39 +0400 Subject: [PATCH 70/88] refactor: change 'devices' to 'device' --- internal/app/enaptercli/cmd_device.go | 22 +++++++++---------- .../enaptercli/cmd_device_change_blueprint.go | 18 +++++++-------- internal/app/enaptercli/cmd_device_command.go | 6 ++--- .../enaptercli/cmd_device_command_execute.go | 4 ++-- .../app/enaptercli/cmd_device_command_get.go | 2 +- .../cmd_device_communication_config.go | 6 ++--- internal/app/enaptercli/cmd_device_delete.go | 14 ++++++------ internal/app/enaptercli/cmd_device_get.go | 18 +++++++-------- internal/app/enaptercli/cmd_device_list.go | 18 +++++++-------- internal/app/enaptercli/cmd_device_logs.go | 18 +++++++-------- .../app/enaptercli/cmd_device_telemetry.go | 4 ++-- internal/app/enaptercli/cmd_device_update.go | 4 ++-- internal/app/enaptercli/execute.go | 2 +- 13 files changed, 68 insertions(+), 68 deletions(-) diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index b62bffa..e41ae70 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -8,24 +8,24 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDevices struct { +type cmdDevice struct { cmdBase } -func buildCmdDevices() *cli.Command { - cmd := &cmdDevices{} +func buildCmdDevice() *cli.Command { + cmd := &cmdDevice{} return &cli.Command{ Name: "device", Usage: "Manage devices", CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ buildCmdDeviceCreate(), - buildCmdDevicesList(), - buildCmdDevicesGet(), - buildCmdDevicesChangeBlueprint(), - buildCmdDevicesLogs(), + buildCmdDeviceList(), + buildCmdDeviceGet(), + buildCmdDeviceChangeBlueprint(), + buildCmdDeviceLogs(), buildCmdDeviceUpdate(), - buildCmdDevicesDelete(), + buildCmdDeviceDelete(), buildCmdDeviceCommand(), buildCmdDeviceTelemetry(), buildCmdDeviceCommunicationConfig(), @@ -33,7 +33,7 @@ func buildCmdDevices() *cli.Command { } } -func (c *cmdDevices) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { +func (c *cmdDevice) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { path, err := url.JoinPath("/devices", p.Path) if err != nil { return fmt.Errorf("join path: %w", err) @@ -42,10 +42,10 @@ func (c *cmdDevices) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) e return c.cmdBase.doHTTPRequest(ctx, p) } -func (c *cmdDevices) validateExpandFlag(cliCtx *cli.Context) error { +func (c *cmdDevice) validateExpandFlag(cliCtx *cli.Context) error { return validateExpandFlag(cliCtx, c.supportedExpandFields()) } -func (c *cmdDevices) supportedExpandFields() []string { +func (c *cmdDevice) supportedExpandFields() []string { return []string{"connectivity", "manifest", "properties", "communication", "site"} } diff --git a/internal/app/enaptercli/cmd_device_change_blueprint.go b/internal/app/enaptercli/cmd_device_change_blueprint.go index 38ec7c4..a8f751e 100644 --- a/internal/app/enaptercli/cmd_device_change_blueprint.go +++ b/internal/app/enaptercli/cmd_device_change_blueprint.go @@ -10,15 +10,15 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDevicesChangeBlueprint struct { - cmdDevices +type cmdDeviceChangeBlueprint struct { + cmdDevice deviceID string blueprintID string blueprintPath string } -func buildCmdDevicesChangeBlueprint() *cli.Command { - cmd := &cmdDevicesChangeBlueprint{} +func buildCmdDeviceChangeBlueprint() *cli.Command { + cmd := &cmdDeviceChangeBlueprint{} return &cli.Command{ Name: "change-blueprint", Usage: "Change blueprint to device", @@ -31,8 +31,8 @@ func buildCmdDevicesChangeBlueprint() *cli.Command { } } -func (c *cmdDevicesChangeBlueprint) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() +func (c *cmdDeviceChangeBlueprint) Flags() []cli.Flag { + flags := c.cmdDevice.Flags() return append(flags, &cli.StringFlag{ Name: "device-id", Aliases: []string{"d"}, @@ -51,8 +51,8 @@ func (c *cmdDevicesChangeBlueprint) Flags() []cli.Flag { }) } -func (c *cmdDevicesChangeBlueprint) Before(cliCtx *cli.Context) error { - if err := c.cmdDevices.Before(cliCtx); err != nil { +func (c *cmdDeviceChangeBlueprint) Before(cliCtx *cli.Context) error { + if err := c.cmdDevice.Before(cliCtx); err != nil { return err } if c.blueprintID != "" && c.blueprintPath != "" { @@ -64,7 +64,7 @@ func (c *cmdDevicesChangeBlueprint) Before(cliCtx *cli.Context) error { return c.validateExpandFlag(cliCtx) } -func (c *cmdDevicesChangeBlueprint) do(ctx context.Context) error { +func (c *cmdDeviceChangeBlueprint) do(ctx context.Context) error { if c.blueprintPath != "" { blueprintID, err := uploadBlueprintAndReturnBlueprintID(ctx, c.blueprintPath, c.cmdBase.doHTTPRequest) if err != nil { diff --git a/internal/app/enaptercli/cmd_device_command.go b/internal/app/enaptercli/cmd_device_command.go index ef200bf..db90388 100644 --- a/internal/app/enaptercli/cmd_device_command.go +++ b/internal/app/enaptercli/cmd_device_command.go @@ -9,7 +9,7 @@ import ( ) type cmdDeviceCommand struct { - cmdDevices + cmdDevice deviceID string } @@ -28,7 +28,7 @@ func buildCmdDeviceCommand() *cli.Command { } func (c *cmdDeviceCommand) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() + flags := c.cmdDevice.Flags() return append(flags, &cli.StringFlag{ Name: "device-id", @@ -46,5 +46,5 @@ func (c *cmdDeviceCommand) doHTTPRequest(ctx context.Context, p doHTTPRequestPar return fmt.Errorf("join path: %w", err) } p.Path = path - return c.cmdDevices.doHTTPRequest(ctx, p) + return c.cmdDevice.doHTTPRequest(ctx, p) } diff --git a/internal/app/enaptercli/cmd_device_command_execute.go b/internal/app/enaptercli/cmd_device_command_execute.go index 919c1b5..a04600a 100644 --- a/internal/app/enaptercli/cmd_device_command_execute.go +++ b/internal/app/enaptercli/cmd_device_command_execute.go @@ -11,7 +11,7 @@ import ( ) type cmdDeviceCommandExecute struct { - cmdDevices + cmdDevice deviceID string cmdName string cmdArgs string @@ -33,7 +33,7 @@ func buildCmdDeviceCommandExecute() *cli.Command { } func (c *cmdDeviceCommandExecute) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() + flags := c.cmdDevice.Flags() return append(flags, &cli.StringFlag{ Name: "device-id", diff --git a/internal/app/enaptercli/cmd_device_command_get.go b/internal/app/enaptercli/cmd_device_command_get.go index af90b31..3ddfb25 100644 --- a/internal/app/enaptercli/cmd_device_command_get.go +++ b/internal/app/enaptercli/cmd_device_command_get.go @@ -48,7 +48,7 @@ func (c *cmdDeviceCommandGet) Flags() []cli.Flag { } func (c *cmdDeviceCommandGet) Before(cliCtx *cli.Context) error { - if err := c.cmdDevices.Before(cliCtx); err != nil { + if err := c.cmdDevice.Before(cliCtx); err != nil { return err } return validateExpandFlag(cliCtx, []string{"log"}) diff --git a/internal/app/enaptercli/cmd_device_communication_config.go b/internal/app/enaptercli/cmd_device_communication_config.go index 6515224..a060118 100644 --- a/internal/app/enaptercli/cmd_device_communication_config.go +++ b/internal/app/enaptercli/cmd_device_communication_config.go @@ -9,7 +9,7 @@ import ( ) type cmdDeviceCommunicationConfig struct { - cmdDevices + cmdDevice deviceID string } @@ -26,7 +26,7 @@ func buildCmdDeviceCommunicationConfig() *cli.Command { } func (c *cmdDeviceCommunicationConfig) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() + flags := c.cmdDevice.Flags() return append(flags, &cli.StringFlag{ Name: "device-id", @@ -44,5 +44,5 @@ func (c *cmdDeviceCommunicationConfig) doHTTPRequest(ctx context.Context, p doHT return fmt.Errorf("join path: %w", err) } p.Path = path - return c.cmdDevices.doHTTPRequest(ctx, p) + return c.cmdDevice.doHTTPRequest(ctx, p) } diff --git a/internal/app/enaptercli/cmd_device_delete.go b/internal/app/enaptercli/cmd_device_delete.go index b655756..67d5ebf 100644 --- a/internal/app/enaptercli/cmd_device_delete.go +++ b/internal/app/enaptercli/cmd_device_delete.go @@ -7,13 +7,13 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDevicesDelete struct { - cmdDevices +type cmdDeviceDelete struct { + cmdDevice deviceID string } -func buildCmdDevicesDelete() *cli.Command { - cmd := &cmdDevicesDelete{} +func buildCmdDeviceDelete() *cli.Command { + cmd := &cmdDeviceDelete{} return &cli.Command{ Name: "delete", Usage: "Delete a device", @@ -26,8 +26,8 @@ func buildCmdDevicesDelete() *cli.Command { } } -func (c *cmdDevicesDelete) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() +func (c *cmdDeviceDelete) Flags() []cli.Flag { + flags := c.cmdDevice.Flags() return append(flags, &cli.StringFlag{ Name: "device-id", @@ -39,7 +39,7 @@ func (c *cmdDevicesDelete) Flags() []cli.Flag { ) } -func (c *cmdDevicesDelete) do(ctx context.Context) error { +func (c *cmdDeviceDelete) do(ctx context.Context) error { return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodDelete, Path: "/" + c.deviceID, diff --git a/internal/app/enaptercli/cmd_device_get.go b/internal/app/enaptercli/cmd_device_get.go index 9dc4edd..86a06d4 100644 --- a/internal/app/enaptercli/cmd_device_get.go +++ b/internal/app/enaptercli/cmd_device_get.go @@ -9,14 +9,14 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDevicesGet struct { - cmdDevices +type cmdDeviceGet struct { + cmdDevice deviceID string expand []string } -func buildCmdDevicesGet() *cli.Command { - cmd := &cmdDevicesGet{} +func buildCmdDeviceGet() *cli.Command { + cmd := &cmdDeviceGet{} return &cli.Command{ Name: "get", Usage: "Retrieve device information", @@ -29,8 +29,8 @@ func buildCmdDevicesGet() *cli.Command { } } -func (c *cmdDevicesGet) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() +func (c *cmdDeviceGet) Flags() []cli.Flag { + flags := c.cmdDevice.Flags() return append(flags, &cli.StringFlag{ Name: "device-id", Aliases: []string{"d"}, @@ -47,14 +47,14 @@ func (c *cmdDevicesGet) Flags() []cli.Flag { }) } -func (c *cmdDevicesGet) Before(cliCtx *cli.Context) error { - if err := c.cmdDevices.Before(cliCtx); err != nil { +func (c *cmdDeviceGet) Before(cliCtx *cli.Context) error { + if err := c.cmdDevice.Before(cliCtx); err != nil { return err } return c.validateExpandFlag(cliCtx) } -func (c *cmdDevicesGet) do(ctx context.Context) error { +func (c *cmdDeviceGet) do(ctx context.Context) error { query := url.Values{} if len(c.expand) != 0 { query.Set("expand", strings.Join(c.expand, ",")) diff --git a/internal/app/enaptercli/cmd_device_list.go b/internal/app/enaptercli/cmd_device_list.go index 92d73e3..528a1f2 100644 --- a/internal/app/enaptercli/cmd_device_list.go +++ b/internal/app/enaptercli/cmd_device_list.go @@ -9,15 +9,15 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDevicesList struct { - cmdDevices +type cmdDeviceList struct { + cmdDevice siteID string expand []string limit int } -func buildCmdDevicesList() *cli.Command { - cmd := &cmdDevicesList{} +func buildCmdDeviceList() *cli.Command { + cmd := &cmdDeviceList{} return &cli.Command{ Name: "list", Usage: "List user devices ordered by device ID", @@ -30,8 +30,8 @@ func buildCmdDevicesList() *cli.Command { } } -func (c *cmdDevicesList) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() +func (c *cmdDeviceList) Flags() []cli.Flag { + flags := c.cmdDevice.Flags() return append(flags, &cli.MultiStringFlag{ Target: &cli.StringSliceFlag{ Name: "expand", @@ -51,14 +51,14 @@ func (c *cmdDevicesList) Flags() []cli.Flag { }) } -func (c *cmdDevicesList) Before(cliCtx *cli.Context) error { - if err := c.cmdDevices.Before(cliCtx); err != nil { +func (c *cmdDeviceList) Before(cliCtx *cli.Context) error { + if err := c.cmdDevice.Before(cliCtx); err != nil { return err } return c.validateExpandFlag(cliCtx) } -func (c *cmdDevicesList) do(ctx context.Context) error { +func (c *cmdDeviceList) do(ctx context.Context) error { query := url.Values{} if len(c.expand) != 0 { query.Set("expand", strings.Join(c.expand, ",")) diff --git a/internal/app/enaptercli/cmd_device_logs.go b/internal/app/enaptercli/cmd_device_logs.go index c0621b5..87c809f 100644 --- a/internal/app/enaptercli/cmd_device_logs.go +++ b/internal/app/enaptercli/cmd_device_logs.go @@ -13,8 +13,8 @@ import ( "github.com/urfave/cli/v2" ) -type cmdDevicesLogs struct { - cmdDevices +type cmdDeviceLogs struct { + cmdDevice deviceID string follow bool from cli.Timestamp @@ -26,8 +26,8 @@ type cmdDevicesLogs struct { showFilter string } -func buildCmdDevicesLogs() *cli.Command { - cmd := &cmdDevicesLogs{} +func buildCmdDeviceLogs() *cli.Command { + cmd := &cmdDeviceLogs{} return &cli.Command{ Name: "logs", Usage: "Show device logs", @@ -40,8 +40,8 @@ func buildCmdDevicesLogs() *cli.Command { } } -func (c *cmdDevicesLogs) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() +func (c *cmdDeviceLogs) Flags() []cli.Flag { + flags := c.cmdDevice.Flags() return append(flags, &cli.StringFlag{ Name: "device-id", Aliases: []string{"d"}, @@ -101,14 +101,14 @@ func (c *cmdDevicesLogs) Flags() []cli.Flag { }) } -func (c *cmdDevicesLogs) do(ctx context.Context) error { +func (c *cmdDeviceLogs) do(ctx context.Context) error { if c.follow { return c.doFollow(ctx) } return c.doList(ctx) } -func (c *cmdDevicesLogs) doFollow(ctx context.Context) error { +func (c *cmdDeviceLogs) doFollow(ctx context.Context) error { if c.from.Value() != nil { return cli.Exit("Option received_at_from is unsupported in follow mode.", 1) } @@ -154,7 +154,7 @@ func (c *cmdDevicesLogs) doFollow(ctx context.Context) error { }) } -func (c *cmdDevicesLogs) doList(ctx context.Context) error { +func (c *cmdDeviceLogs) doList(ctx context.Context) error { query := url.Values{} if c.from.Value() != nil { query.Add("received_at_from", c.from.Value().Format(time.RFC3339)) diff --git a/internal/app/enaptercli/cmd_device_telemetry.go b/internal/app/enaptercli/cmd_device_telemetry.go index 0ac7db6..a030cfb 100644 --- a/internal/app/enaptercli/cmd_device_telemetry.go +++ b/internal/app/enaptercli/cmd_device_telemetry.go @@ -11,7 +11,7 @@ import ( ) type cmdDeviceTelemetry struct { - cmdDevices + cmdDevice deviceID string follow bool } @@ -31,7 +31,7 @@ func buildCmdDeviceTelemetry() *cli.Command { } func (c *cmdDeviceTelemetry) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() + flags := c.cmdDevice.Flags() return append(flags, &cli.StringFlag{ Name: "device-id", Aliases: []string{"d"}, diff --git a/internal/app/enaptercli/cmd_device_update.go b/internal/app/enaptercli/cmd_device_update.go index 0ba062e..a4ebb31 100644 --- a/internal/app/enaptercli/cmd_device_update.go +++ b/internal/app/enaptercli/cmd_device_update.go @@ -11,7 +11,7 @@ import ( ) type cmdDeviceUpdate struct { - cmdDevices + cmdDevice deviceID string name string slug string @@ -32,7 +32,7 @@ func buildCmdDeviceUpdate() *cli.Command { } func (c *cmdDeviceUpdate) Flags() []cli.Flag { - flags := c.cmdDevices.Flags() + flags := c.cmdDevice.Flags() return append(flags, &cli.StringFlag{ Name: "device-id", diff --git a/internal/app/enaptercli/execute.go b/internal/app/enaptercli/execute.go index c8cdefd..777a728 100644 --- a/internal/app/enaptercli/execute.go +++ b/internal/app/enaptercli/execute.go @@ -23,7 +23,7 @@ func NewApp() *cli.App { app.Commands = []*cli.Command{ buildCmdSites(), - buildCmdDevices(), + buildCmdDevice(), buildCmdBlueprints(), buildCmdRuleEngine(), } From db2e66a73e94bde19d41ac9c5f068793f909a245 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Mon, 8 Sep 2025 19:26:51 +0400 Subject: [PATCH 71/88] Merge master into v3 branch Resolve merge conflicts to enable CI checks. Squashed commit of the following: commit 36d65cfc4bf092fdbb9c2551e57620d73bd710f1 Author: Nikolay V. Krasko Date: Fri Sep 5 15:52:41 2025 +0400 adding notes with status commit 2277748fee99d94d4a374646a0b266377d4fba9c Author: Nikolay V. Krasko Date: Fri Sep 5 15:43:37 2025 +0400 addding enapter3 cli usage description --- README.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a32a695..5fc04fd 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,26 @@ [![Release](https://img.shields.io/github/release/enapter/enapter-cli.svg)](https://github.com/enapter/enapter-cli/releases/latest) -This tool helps Enapter customers to work with devices. It useful in the following cases: -1. Develop devices via blueprints. -2. Update and monitor devices. +This tool helps Enapter customers to work with devices it is alternative for [Enapter IDE for EMS Toolkit 3.0](https://marketplace.visualstudio.com/items?itemName=Enapter.enapter-ems-toolkit-ide). +It helpful in the following cases: + +1. Managing all your EMS setup as a code with Git and Ansible / Puppet +2. Establishing CI/CD workflow +3. Development and debugging of Enapter Blueprints +4. Development and debugging of Enapter Gateway Rules ## How to install ###  macOS - recommended +Version 1: + +```bash +brew tap enapter/tap && brew install enapter +``` + +Version 3: + ```bash brew tap enapter/tap && brew install enapter@3 ``` @@ -32,7 +44,10 @@ Also you can pass custom output path: ./build.sh /usr/local/bin/enapter ``` -## How to use +## How to use Version 1: + +> [!NOTE] +> Version 1 works only with Enapter Cloud connection. ### API token @@ -52,8 +67,35 @@ Enapter CLI requires access token for authentication. Obtaining of the token is Please note that if you don't save your token, it is not possible to reveal it anymore. You need generate new token. +## How to use Version 3: + +### API token + +Enapter CLI requires access token for authentication. Obtaining of the token is easy and can be done by following few steps. + +1. Navigate to your Enapter Gateway 3.0 Web Interface `Settings` page by using IP address or mDNS name [http://enapter-gateway.local/settings](https://enapter-gateway.local/settings) +2. Enapter your Enapter Gateway password +3. Click `API Token` and copy token to clipboard +4. Set environment variables `ENAPTER3_API_TOKEN`, `ENAPTER3_API_URL` and `ENAPTER3_API_ALLOW_INSECURE`. To make it permanent don't forget to add it to configuration files of your shell. + + ```bash + export ENAPTER3_API_TOKEN="your token" + export ENAPTER3_API_URL="http://ip_address/api" + export ENAPTER3_API_ALLOW_INSECURE=true + ``` + +5. Check connection works by running + + ```bash + enapter3 device list + ``` + ### Autocompletion in your favourite terminal app -In order to make life easier with command line interface, you may use [Fig - the next-generation command line](https://fig.io/). This autocompletion tool has native support for the Enapter CLI for Mac OS X and Linux. +> [!NOTE] +> Available for Version 1 now. + +In order to make life easier with command line interface, you may use [Amazon Q](https://aws.amazon.com/q/). This autocompletion tool has native support for the Enapter CLI for Mac OS X and Linux. + From 97a3dfa81f6ff50334d605351f7252aecc14e74b Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Mon, 8 Sep 2025 20:14:03 +0400 Subject: [PATCH 72/88] feat: print downloaded file name --- internal/app/enaptercli/cmd_blueprints_download.go | 1 + internal/app/enaptercli/cmd_blueprints_profiles_download.go | 1 + 2 files changed, 2 insertions(+) diff --git a/internal/app/enaptercli/cmd_blueprints_download.go b/internal/app/enaptercli/cmd_blueprints_download.go index ff2efa9..80030a9 100644 --- a/internal/app/enaptercli/cmd_blueprints_download.go +++ b/internal/app/enaptercli/cmd_blueprints_download.go @@ -90,6 +90,7 @@ func (c *cmdBlueprintsDownload) do(ctx context.Context) error { if _, err := io.Copy(outFile, body); err != nil { return fmt.Errorf("write output file %q: %w", c.outputFileName, err) } + fmt.Fprintln(c.writer, c.outputFileName) return nil }), }) diff --git a/internal/app/enaptercli/cmd_blueprints_profiles_download.go b/internal/app/enaptercli/cmd_blueprints_profiles_download.go index 5efd9ee..520f694 100644 --- a/internal/app/enaptercli/cmd_blueprints_profiles_download.go +++ b/internal/app/enaptercli/cmd_blueprints_profiles_download.go @@ -56,6 +56,7 @@ func (c *cmdBlueprintsProfilesDownload) do(ctx context.Context) error { if _, err := io.Copy(outFile, body); err != nil { return fmt.Errorf("write output file %q: %w", c.outputFileName, err) } + fmt.Fprintln(c.writer, c.outputFileName) return nil }), }) From add51fa19e4b1f7604747a564b8978f1bddd27ac Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Mon, 3 Nov 2025 15:05:01 +0400 Subject: [PATCH 73/88] feat: expect logs timestamp as int64 --- internal/app/enaptercli/cmd_device_logs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/app/enaptercli/cmd_device_logs.go b/internal/app/enaptercli/cmd_device_logs.go index 87c809f..020bf19 100644 --- a/internal/app/enaptercli/cmd_device_logs.go +++ b/internal/app/enaptercli/cmd_device_logs.go @@ -140,8 +140,8 @@ func (c *cmdDeviceLogs) doFollow(ctx context.Context) error { Query: query, RespProcessor: func(r io.Reader) error { var msg struct { + Timestamp int64 `json:"timestamp"` ReceivedAt string `json:"received_at"` - Timestamp string `json:"timestamp"` Severity string `json:"severity"` Message string `json:"message"` } @@ -186,8 +186,8 @@ func (c *cmdDeviceLogs) doList(ctx context.Context) error { RespProcessor: okRespBodyProcessor(func(body io.Reader) error { var resp struct { Logs []struct { + Timestamp int64 `json:"timestamp"` ReceivedAt string `json:"received_at"` - Timestamp string `json:"timestamp"` Severity string `json:"severity"` Message string `json:"message"` } `json:"logs"` From 7ae5c8feeaacddd37e1e1d2deb6ff1dfab388e2c Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Mon, 3 Nov 2025 16:57:27 +0400 Subject: [PATCH 74/88] feat: add custom user agent header --- internal/app/enaptercli/cmd_base.go | 8 ++++++-- .../testdata/http_req_resp/blueprint_inspect_by_id/req_0 | 2 +- .../http_req_resp/blueprint_inspect_by_name/req_0 | 2 +- .../http_req_resp/device_assign_blueprint_by_id/req_0 | 2 +- .../http_req_resp/device_assign_blueprint_by_path/req_0 | 2 +- .../http_req_resp/device_assign_blueprint_by_path/req_1 | 2 +- .../http_req_resp/device_assign_blueprint_by_zip/req_0 | 2 +- .../http_req_resp/device_assign_blueprint_by_zip/req_1 | 2 +- 8 files changed, 13 insertions(+), 9 deletions(-) diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index d7192c2..af7e3e7 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -22,6 +22,7 @@ import ( type cmdBase struct { verbose bool token string + userAgent string apiHost string apiAllowInsecure bool writer io.Writer @@ -67,6 +68,7 @@ func (c *cmdBase) Before(cliCtx *cli.Context) error { if cliCtx.String("token") == "" { return errAPITokenMissed } + c.userAgent = "enapter-cli/" + cliCtx.App.Version c.writer = cliCtx.App.Writer c.errWriter = cliCtx.App.ErrWriter c.httpClient = &http.Client{ @@ -110,7 +112,8 @@ func (c *cmdBase) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) erro return fmt.Errorf("build http request: %w", err) } - req.Header.Add("X-Enapter-Auth-Token", c.token) + req.Header.Set("X-Enapter-Auth-Token", c.token) + req.Header.Set("User-Agent", c.userAgent) req.Header.Set("Content-Type", p.ContentType) req.URL.RawQuery = p.Query.Encode() @@ -211,7 +214,8 @@ func (c *cmdBase) dialWebSocket( url.RawQuery = query.Encode() headers := make(http.Header) - headers.Add("X-Enapter-Auth-Token", c.token) + headers.Set("X-Enapter-Auth-Token", c.token) + headers.Set("User-Agent", c.userAgent) const timeout = 5 * time.Second dialer := websocket.Dialer{ diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/req_0 index 75d3ce7..65ad234 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/req_0 +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/req_0 @@ -9,7 +9,7 @@ "" ], "User-Agent": [ - "Go-http-client/1.1" + "enapter-cli/" ], "X-Enapter-Auth-Token": [ "enapter_api_test_token" diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/req_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/req_0 index 89ac6aa..72fc047 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/req_0 +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/req_0 @@ -9,7 +9,7 @@ "" ], "User-Agent": [ - "Go-http-client/1.1" + "enapter-cli/" ], "X-Enapter-Auth-Token": [ "enapter_api_test_token" diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/req_0 index 490ced4..0a7f3c4 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/req_0 +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/req_0 @@ -12,7 +12,7 @@ "application/json" ], "User-Agent": [ - "Go-http-client/1.1" + "enapter-cli/" ], "X-Enapter-Auth-Token": [ "enapter_api_test_token" diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_0 index 8fe0705..0a841e2 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_0 +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_0 @@ -12,7 +12,7 @@ "" ], "User-Agent": [ - "Go-http-client/1.1" + "enapter-cli/" ], "X-Enapter-Auth-Token": [ "enapter_api_test_token" diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_1 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_1 index b2be2c4..26711ab 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_1 +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_1 @@ -12,7 +12,7 @@ "application/json" ], "User-Agent": [ - "Go-http-client/1.1" + "enapter-cli/" ], "X-Enapter-Auth-Token": [ "enapter_api_test_token" diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_0 index b91ee14..7175cac 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_0 +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_0 @@ -12,7 +12,7 @@ "" ], "User-Agent": [ - "Go-http-client/1.1" + "enapter-cli/" ], "X-Enapter-Auth-Token": [ "enapter_api_test_token" diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_1 b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_1 index 141edbe..8e5669b 100644 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_1 +++ b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_1 @@ -12,7 +12,7 @@ "application/json" ], "User-Agent": [ - "Go-http-client/1.1" + "enapter-cli/" ], "X-Enapter-Auth-Token": [ "enapter_api_test_token" From 3141bc2a5f008c97f20399ce1471746f2ab5ebd9 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Mon, 3 Nov 2025 17:04:18 +0400 Subject: [PATCH 75/88] chore: migrate to golangci-lint v2 --- .github/workflows/ci.yml | 4 +- .golangci.yml | 102 ++++++++---------- .../cmd_device_create_lua_device.go | 2 +- 3 files changed, 49 insertions(+), 59 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11a51b2..5d19384 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,6 @@ jobs: with: go-version: ~1.23 - name: golangci-lint - uses: golangci/golangci-lint-action@v6 + uses: golangci/golangci-lint-action@v8 with: - version: v1.64.8 + version: v2.5.0 diff --git a/.golangci.yml b/.golangci.yml index 60abab6..f6ec344 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,24 +1,16 @@ -run: - timeout: 5m - +version: "2" linters: - disable-all: true + default: none enable: - asciicheck - bodyclose - copyloopvar - # - deadcode - # - depguard - dogsled - # - dupl # some commands are looks very similar in implementation - err113 - errcheck - errorlint - exhaustive - # - exhaustivestruct - # - exportloopref - funlen - - gci - gochecknoglobals - gochecknoinits - gocognit @@ -26,74 +18,72 @@ linters: - gocritic - gocyclo - godot - # - godox - - gofmt - - gofumpt - goheader - - goimports - # - golint - gomodguard - goprintffuncname - gosec - - gosimple - govet - ineffassign - # - interfacer # is prone to bad suggestions (officialy deprecated) - lll - # - maligned - misspell - mnd - nakedret - nestif - # - nlreturn - noctx - nolintlint - prealloc - revive - rowserrcheck - # - scopelint - sqlclosecheck - staticcheck - # - structcheck - - stylecheck - testpackage - tparallel - - typecheck - unconvert - unparam - unused - # - varcheck - whitespace - # - wrapcheck - # - wsl - -linters-settings: - lll: - line-length: 110 - gci: + settings: + lll: + line-length: 110 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - gosec + path: _test\.go + - linters: + - lll + source: '^//go:generate ' + - linters: + - lll + source: '^import ' + - linters: + - lll + source: //.*(http|https):// + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gci + - gofmt + - gofumpt + - goimports + settings: + gci: sections: - - standard - - default - - prefix(github.com/enapter/enapter-cli/) - -issues: - exclude-rules: - # Exclude gosec from running on tests files because this makes no sense. - - path: _test\.go - linters: - - gosec - - # Exclude lll issues for long lines with go:generate. - - linters: - - lll - source: "^//go:generate " - - # Import paths can be long. - - linters: - - lll - source: "^import " - - # Links to articles can be long. - - linters: - - lll - source: "//.*(http|https)://" + - standard + - default + - prefix(github.com/enapter/enapter-cli/) + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/internal/app/enaptercli/cmd_device_create_lua_device.go b/internal/app/enaptercli/cmd_device_create_lua_device.go index a72ad67..4968e2c 100644 --- a/internal/app/enaptercli/cmd_device_create_lua_device.go +++ b/internal/app/enaptercli/cmd_device_create_lua_device.go @@ -78,7 +78,7 @@ func (c *cmdDeviceCreateLua) Before(cliCtx *cli.Context) error { func (c *cmdDeviceCreateLua) do(ctx context.Context) error { if c.blueprintPath != "" { - blueprintID, err := uploadBlueprintAndReturnBlueprintID(ctx, c.blueprintPath, c.cmdBase.doHTTPRequest) + blueprintID, err := uploadBlueprintAndReturnBlueprintID(ctx, c.blueprintPath, c.doHTTPRequest) if err != nil { return fmt.Errorf("upload blueprint: %w", err) } From 26e7e84b8e8f63d7af79e4df6418abf89dc07339 Mon Sep 17 00:00:00 2001 From: Roman Novatorov Date: Fri, 19 Sep 2025 11:23:32 +0200 Subject: [PATCH 76/88] cmd device change-blueprint: fix typo in description --- internal/app/enaptercli/cmd_device_change_blueprint.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/app/enaptercli/cmd_device_change_blueprint.go b/internal/app/enaptercli/cmd_device_change_blueprint.go index a8f751e..9ad3271 100644 --- a/internal/app/enaptercli/cmd_device_change_blueprint.go +++ b/internal/app/enaptercli/cmd_device_change_blueprint.go @@ -21,7 +21,7 @@ func buildCmdDeviceChangeBlueprint() *cli.Command { cmd := &cmdDeviceChangeBlueprint{} return &cli.Command{ Name: "change-blueprint", - Usage: "Change blueprint to device", + Usage: "Change device blueprint", CustomHelpTemplate: cmd.CommandHelpTemplate(), Flags: cmd.Flags(), Before: cmd.Before, From 6fbb2198a83957a3ac37c49be60a079d7b8c7999 Mon Sep 17 00:00:00 2001 From: Roman Novatorov Date: Mon, 8 Dec 2025 15:12:28 +0100 Subject: [PATCH 77/88] fix tests of help messages --- internal/app/enaptercli/testdata/helps/enapter device | 2 +- .../enaptercli/testdata/helps/enapter device change-blueprint | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index 7901a83..0ba3641 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -8,7 +8,7 @@ COMMANDS: create Create devices of different types list List user devices ordered by device ID get Retrieve device information - change-blueprint Change blueprint to device + change-blueprint Change device blueprint logs Show device logs update Update a device delete Delete a device diff --git a/internal/app/enaptercli/testdata/helps/enapter device change-blueprint b/internal/app/enaptercli/testdata/helps/enapter device change-blueprint index 0189840..d4591a0 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device change-blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter device change-blueprint @@ -1,5 +1,5 @@ NAME: - enaptercli.test device change-blueprint - Change blueprint to device + enaptercli.test device change-blueprint - Change device blueprint USAGE: enaptercli.test device change-blueprint [command options] From 2b0de71b0be560d39868e0e418bb7007d565ad75 Mon Sep 17 00:00:00 2001 From: Roman Novatorov Date: Mon, 8 Dec 2025 15:14:23 +0100 Subject: [PATCH 78/88] ci: run on `v3` branch --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d19384..de1f7fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [master] + branches: [master, v3] pull_request: - branches: [master] + branches: [master, v3] jobs: tests: From 2b3af02157ba77cfc496e595a6fa3e187a67f24b Mon Sep 17 00:00:00 2001 From: Roman Novatorov Date: Mon, 8 Dec 2025 18:55:21 +0100 Subject: [PATCH 79/88] device: create-standalone: add `--device-slug` option --- internal/app/enaptercli/cmd_device_create_standalone.go | 6 ++++++ .../testdata/helps/enapter device create standalone | 1 + 2 files changed, 7 insertions(+) diff --git a/internal/app/enaptercli/cmd_device_create_standalone.go b/internal/app/enaptercli/cmd_device_create_standalone.go index 7230eb6..b9ce065 100644 --- a/internal/app/enaptercli/cmd_device_create_standalone.go +++ b/internal/app/enaptercli/cmd_device_create_standalone.go @@ -14,6 +14,7 @@ type cmdDeviceCreateStandalone struct { cmdDeviceCreate siteID string deviceName string + deviceSlug string } func buildCmdDeviceCreateStandalone() *cli.Command { @@ -43,6 +44,10 @@ func (c *cmdDeviceCreateStandalone) Flags() []cli.Flag { Usage: "name for the new device", Destination: &c.deviceName, Required: true, + }, &cli.StringFlag{ + Name: "device-slug", + Usage: "slug for the new standalone device", + Destination: &c.deviceSlug, }) } @@ -50,6 +55,7 @@ func (c *cmdDeviceCreateStandalone) do(ctx context.Context) error { body, err := json.Marshal(map[string]any{ "site_id": c.siteID, "name": c.deviceName, + "slug": c.deviceSlug, }) if err != nil { return fmt.Errorf("build request: %w", err) diff --git a/internal/app/enaptercli/testdata/helps/enapter device create standalone b/internal/app/enaptercli/testdata/helps/enapter device create standalone index 75e0c76..b8e9f0e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device create standalone +++ b/internal/app/enaptercli/testdata/helps/enapter device create standalone @@ -10,6 +10,7 @@ OPTIONS: --verbose log extra details about the operation (default: false) --site-id value, -s value site ID where the device will be created --device-name value, -n value name for the new device + --device-slug value slug for the new standalone device --help, -h show help ENVIRONMENT VARIABLES: From 78f264eb3902a5e2e5319fac8255c286f279a971 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Wed, 10 Dec 2025 18:33:05 +0400 Subject: [PATCH 80/88] refactor: consistent use of the singular form --- .../{cmd_blueprints.go => cmd_blueprint.go} | 14 +++++++------- ...rints_download.go => cmd_blueprint_download.go} | 14 +++++++------- ...{cmd_blueprints_get.go => cmd_blueprint_get.go} | 14 +++++++------- ...rints_profiles.go => cmd_blueprint_profiles.go} | 10 +++++----- ...nload.go => cmd_blueprint_profiles_download.go} | 14 +++++++------- ..._upload.go => cmd_blueprint_profiles_upload.go} | 14 +++++++------- ...lueprints_upload.go => cmd_blueprint_upload.go} | 14 +++++++------- internal/app/enaptercli/cmd_site.go | 4 ++-- internal/app/enaptercli/cmd_site_list.go | 10 +++++----- internal/app/enaptercli/execute.go | 4 ++-- 10 files changed, 56 insertions(+), 56 deletions(-) rename internal/app/enaptercli/{cmd_blueprints.go => cmd_blueprint.go} (81%) rename internal/app/enaptercli/{cmd_blueprints_download.go => cmd_blueprint_download.go} (89%) rename internal/app/enaptercli/{cmd_blueprints_get.go => cmd_blueprint_get.go} (79%) rename internal/app/enaptercli/{cmd_blueprints_profiles.go => cmd_blueprint_profiles.go} (57%) rename internal/app/enaptercli/{cmd_blueprints_profiles_download.go => cmd_blueprint_profiles_download.go} (79%) rename internal/app/enaptercli/{cmd_blueprints_profiles_upload.go => cmd_blueprint_profiles_upload.go} (73%) rename internal/app/enaptercli/{cmd_blueprints_upload.go => cmd_blueprint_upload.go} (89%) diff --git a/internal/app/enaptercli/cmd_blueprints.go b/internal/app/enaptercli/cmd_blueprint.go similarity index 81% rename from internal/app/enaptercli/cmd_blueprints.go rename to internal/app/enaptercli/cmd_blueprint.go index 14bb51d..62f563a 100644 --- a/internal/app/enaptercli/cmd_blueprints.go +++ b/internal/app/enaptercli/cmd_blueprint.go @@ -6,21 +6,21 @@ import ( "github.com/urfave/cli/v2" ) -type cmdBlueprints struct { +type cmdBlueprint struct { cmdBase } -func buildCmdBlueprints() *cli.Command { - cmd := &cmdBlueprints{} +func buildCmdBlueprint() *cli.Command { + cmd := &cmdBlueprint{} return &cli.Command{ Name: "blueprint", Usage: "Manage blueprints", CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ - buildCmdBlueprintsProfiles(), - buildCmdBlueprintsUpload(), - buildCmdBlueprintsDownload(), - buildCmdBlueprintsGet(), + buildCmdBlueprintProfiles(), + buildCmdBlueprintUpload(), + buildCmdBlueprintDownload(), + buildCmdBlueprintGet(), }, } } diff --git a/internal/app/enaptercli/cmd_blueprints_download.go b/internal/app/enaptercli/cmd_blueprint_download.go similarity index 89% rename from internal/app/enaptercli/cmd_blueprints_download.go rename to internal/app/enaptercli/cmd_blueprint_download.go index 80030a9..346d8a1 100644 --- a/internal/app/enaptercli/cmd_blueprints_download.go +++ b/internal/app/enaptercli/cmd_blueprint_download.go @@ -12,14 +12,14 @@ import ( "github.com/urfave/cli/v2" ) -type cmdBlueprintsDownload struct { - cmdBlueprints +type cmdBlueprintDownload struct { + cmdBlueprint blueprintID string outputFileName string } -func buildCmdBlueprintsDownload() *cli.Command { - cmd := &cmdBlueprintsDownload{} +func buildCmdBlueprintDownload() *cli.Command { + cmd := &cmdBlueprintDownload{} return &cli.Command{ Name: "download", Usage: "Download the blueprint zip from the Platform", @@ -32,8 +32,8 @@ func buildCmdBlueprintsDownload() *cli.Command { } } -func (c *cmdBlueprintsDownload) Flags() []cli.Flag { - flags := c.cmdBlueprints.Flags() +func (c *cmdBlueprintDownload) Flags() []cli.Flag { + flags := c.cmdBlueprint.Flags() return append(flags, &cli.StringFlag{ Name: "blueprint-id", Aliases: []string{"b"}, @@ -48,7 +48,7 @@ func (c *cmdBlueprintsDownload) Flags() []cli.Flag { }) } -func (c *cmdBlueprintsDownload) do(ctx context.Context) error { +func (c *cmdBlueprintDownload) do(ctx context.Context) error { if c.outputFileName == "" { c.outputFileName = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(c.blueprintID, ":", "_"), ".", "_"), "/", "_") + ".enbp" diff --git a/internal/app/enaptercli/cmd_blueprints_get.go b/internal/app/enaptercli/cmd_blueprint_get.go similarity index 79% rename from internal/app/enaptercli/cmd_blueprints_get.go rename to internal/app/enaptercli/cmd_blueprint_get.go index 03f6cea..09153c0 100644 --- a/internal/app/enaptercli/cmd_blueprints_get.go +++ b/internal/app/enaptercli/cmd_blueprint_get.go @@ -7,13 +7,13 @@ import ( "github.com/urfave/cli/v2" ) -type cmdBlueprintsGet struct { - cmdBlueprints +type cmdBlueprintGet struct { + cmdBlueprint blueprintID string } -func buildCmdBlueprintsGet() *cli.Command { - cmd := &cmdBlueprintsGet{} +func buildCmdBlueprintGet() *cli.Command { + cmd := &cmdBlueprintGet{} return &cli.Command{ Name: "get", Usage: "Retrieve blueprint metadata", @@ -26,8 +26,8 @@ func buildCmdBlueprintsGet() *cli.Command { } } -func (c *cmdBlueprintsGet) Flags() []cli.Flag { - flags := c.cmdBlueprints.Flags() +func (c *cmdBlueprintGet) Flags() []cli.Flag { + flags := c.cmdBlueprint.Flags() return append(flags, &cli.StringFlag{ Name: "blueprint-id", Aliases: []string{"b"}, @@ -37,7 +37,7 @@ func (c *cmdBlueprintsGet) Flags() []cli.Flag { }) } -func (c *cmdBlueprintsGet) get(ctx context.Context) error { +func (c *cmdBlueprintGet) get(ctx context.Context) error { if isBlueprintID(c.blueprintID) { return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, diff --git a/internal/app/enaptercli/cmd_blueprints_profiles.go b/internal/app/enaptercli/cmd_blueprint_profiles.go similarity index 57% rename from internal/app/enaptercli/cmd_blueprints_profiles.go rename to internal/app/enaptercli/cmd_blueprint_profiles.go index 0fcdb0e..8976de7 100644 --- a/internal/app/enaptercli/cmd_blueprints_profiles.go +++ b/internal/app/enaptercli/cmd_blueprint_profiles.go @@ -4,19 +4,19 @@ import ( "github.com/urfave/cli/v2" ) -type cmdBlueprintsProfiles struct { +type cmdBlueprintProfiles struct { cmdBase } -func buildCmdBlueprintsProfiles() *cli.Command { - cmd := &cmdBlueprintsProfiles{} +func buildCmdBlueprintProfiles() *cli.Command { + cmd := &cmdBlueprintProfiles{} return &cli.Command{ Name: "profiles", Usage: "Manage blueprint profiles", CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ - buildCmdBlueprintsProfilesDownload(), - buildCmdBlueprintsProfilesUpload(), + buildCmdBlueprintProfilesDownload(), + buildCmdBlueprintProfilesUpload(), }, } } diff --git a/internal/app/enaptercli/cmd_blueprints_profiles_download.go b/internal/app/enaptercli/cmd_blueprint_profiles_download.go similarity index 79% rename from internal/app/enaptercli/cmd_blueprints_profiles_download.go rename to internal/app/enaptercli/cmd_blueprint_profiles_download.go index 520f694..130f273 100644 --- a/internal/app/enaptercli/cmd_blueprints_profiles_download.go +++ b/internal/app/enaptercli/cmd_blueprint_profiles_download.go @@ -10,13 +10,13 @@ import ( "github.com/urfave/cli/v2" ) -type cmdBlueprintsProfilesDownload struct { - cmdBlueprintsProfiles +type cmdBlueprintProfilesDownload struct { + cmdBlueprintProfiles outputFileName string } -func buildCmdBlueprintsProfilesDownload() *cli.Command { - cmd := &cmdBlueprintsProfilesDownload{} +func buildCmdBlueprintProfilesDownload() *cli.Command { + cmd := &cmdBlueprintProfilesDownload{} return &cli.Command{ Name: "download", Usage: "Download profiles zip from the Platform", @@ -29,8 +29,8 @@ func buildCmdBlueprintsProfilesDownload() *cli.Command { } } -func (c *cmdBlueprintsProfilesDownload) Flags() []cli.Flag { - flags := c.cmdBlueprintsProfiles.Flags() +func (c *cmdBlueprintProfilesDownload) Flags() []cli.Flag { + flags := c.cmdBlueprintProfiles.Flags() return append(flags, &cli.StringFlag{ Name: "output", Aliases: []string{"o"}, @@ -39,7 +39,7 @@ func (c *cmdBlueprintsProfilesDownload) Flags() []cli.Flag { }) } -func (c *cmdBlueprintsProfilesDownload) do(ctx context.Context) error { +func (c *cmdBlueprintProfilesDownload) do(ctx context.Context) error { if c.outputFileName == "" { c.outputFileName = "profiles.zip" } diff --git a/internal/app/enaptercli/cmd_blueprints_profiles_upload.go b/internal/app/enaptercli/cmd_blueprint_profiles_upload.go similarity index 73% rename from internal/app/enaptercli/cmd_blueprints_profiles_upload.go rename to internal/app/enaptercli/cmd_blueprint_profiles_upload.go index 719ce06..bafbe19 100644 --- a/internal/app/enaptercli/cmd_blueprints_profiles_upload.go +++ b/internal/app/enaptercli/cmd_blueprint_profiles_upload.go @@ -10,13 +10,13 @@ import ( "github.com/urfave/cli/v2" ) -type cmdBlueprintsProfilesUpload struct { - cmdBlueprintsProfiles +type cmdBlueprintProfilesUpload struct { + cmdBlueprintProfiles profilesPath string } -func buildCmdBlueprintsProfilesUpload() *cli.Command { - cmd := &cmdBlueprintsProfilesUpload{} +func buildCmdBlueprintProfilesUpload() *cli.Command { + cmd := &cmdBlueprintProfilesUpload{} return &cli.Command{ Name: "upload", Usage: "Upload profiles to the Platform", @@ -29,8 +29,8 @@ func buildCmdBlueprintsProfilesUpload() *cli.Command { } } -func (c *cmdBlueprintsProfilesUpload) Flags() []cli.Flag { - flags := c.cmdBlueprintsProfiles.Flags() +func (c *cmdBlueprintProfilesUpload) Flags() []cli.Flag { + flags := c.cmdBlueprintProfiles.Flags() return append(flags, &cli.StringFlag{ Name: "path", Aliases: []string{"p"}, @@ -40,7 +40,7 @@ func (c *cmdBlueprintsProfilesUpload) Flags() []cli.Flag { }) } -func (c *cmdBlueprintsProfilesUpload) upload(ctx context.Context) error { +func (c *cmdBlueprintProfilesUpload) upload(ctx context.Context) error { data, err := os.ReadFile(c.profilesPath) if err != nil { return fmt.Errorf("read zip file: %w", err) diff --git a/internal/app/enaptercli/cmd_blueprints_upload.go b/internal/app/enaptercli/cmd_blueprint_upload.go similarity index 89% rename from internal/app/enaptercli/cmd_blueprints_upload.go rename to internal/app/enaptercli/cmd_blueprint_upload.go index fee24fb..ae02529 100644 --- a/internal/app/enaptercli/cmd_blueprints_upload.go +++ b/internal/app/enaptercli/cmd_blueprint_upload.go @@ -11,13 +11,13 @@ import ( "github.com/urfave/cli/v2" ) -type cmdBlueprintsUpload struct { - cmdBlueprints +type cmdBlueprintUpload struct { + cmdBlueprint blueprintPath string } -func buildCmdBlueprintsUpload() *cli.Command { - cmd := &cmdBlueprintsUpload{} +func buildCmdBlueprintUpload() *cli.Command { + cmd := &cmdBlueprintUpload{} return &cli.Command{ Name: "upload", Usage: "Upload the blueprint to the Platform", @@ -30,8 +30,8 @@ func buildCmdBlueprintsUpload() *cli.Command { } } -func (c *cmdBlueprintsUpload) Flags() []cli.Flag { - flags := c.cmdBlueprints.Flags() +func (c *cmdBlueprintUpload) Flags() []cli.Flag { + flags := c.cmdBlueprint.Flags() return append(flags, &cli.StringFlag{ Name: "path", Aliases: []string{"p"}, @@ -41,7 +41,7 @@ func (c *cmdBlueprintsUpload) Flags() []cli.Flag { }) } -func (c *cmdBlueprintsUpload) upload(ctx context.Context) error { +func (c *cmdBlueprintUpload) upload(ctx context.Context) error { return uploadBlueprint(ctx, c.blueprintPath, c.doHTTPRequest) } diff --git a/internal/app/enaptercli/cmd_site.go b/internal/app/enaptercli/cmd_site.go index 6ed356d..75fc336 100644 --- a/internal/app/enaptercli/cmd_site.go +++ b/internal/app/enaptercli/cmd_site.go @@ -12,14 +12,14 @@ type cmdSite struct { cmdBase } -func buildCmdSites() *cli.Command { +func buildCmdSite() *cli.Command { cmd := &cmdSite{} return &cli.Command{ Name: "site", Usage: "Manage sites", CustomHelpTemplate: cmd.SubcommandHelpTemplate(), Subcommands: []*cli.Command{ - buildCmdSitesList(), + buildCmdSiteList(), buildCmdSiteGet(), }, } diff --git a/internal/app/enaptercli/cmd_site_list.go b/internal/app/enaptercli/cmd_site_list.go index ec58938..2290e39 100644 --- a/internal/app/enaptercli/cmd_site_list.go +++ b/internal/app/enaptercli/cmd_site_list.go @@ -7,14 +7,14 @@ import ( "github.com/urfave/cli/v2" ) -type cmdSitesList struct { +type cmdSiteList struct { cmdSite mySites bool limit int } -func buildCmdSitesList() *cli.Command { - cmd := &cmdSitesList{} +func buildCmdSiteList() *cli.Command { + cmd := &cmdSiteList{} return &cli.Command{ Name: "list", Usage: "List user sites", @@ -27,7 +27,7 @@ func buildCmdSitesList() *cli.Command { } } -func (c *cmdSitesList) Flags() []cli.Flag { +func (c *cmdSiteList) Flags() []cli.Flag { flags := c.cmdSite.Flags() return append(flags, &cli.BoolFlag{ Name: "my-sites", @@ -41,7 +41,7 @@ func (c *cmdSitesList) Flags() []cli.Flag { }) } -func (c *cmdSitesList) do(ctx context.Context) error { +func (c *cmdSiteList) do(ctx context.Context) error { doPaginateRequestParams := paginateHTTPRequestParams{ ObjectName: "sites", Limit: c.limit, diff --git a/internal/app/enaptercli/execute.go b/internal/app/enaptercli/execute.go index 777a728..584e313 100644 --- a/internal/app/enaptercli/execute.go +++ b/internal/app/enaptercli/execute.go @@ -22,9 +22,9 @@ func NewApp() *cli.App { app.CustomAppHelpTemplate = cli.AppHelpTemplate + enapterAPIEnvVarsHelp app.Commands = []*cli.Command{ - buildCmdSites(), + buildCmdSite(), buildCmdDevice(), - buildCmdBlueprints(), + buildCmdBlueprint(), buildCmdRuleEngine(), } From 658124e8bf67e228c5ee5ef8d90811bbfeb04777 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Wed, 10 Dec 2025 19:05:07 +0400 Subject: [PATCH 81/88] fix: broken paths on Windows --- internal/app/enaptercli/execute.go | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/internal/app/enaptercli/execute.go b/internal/app/enaptercli/execute.go index 584e313..0f13862 100644 --- a/internal/app/enaptercli/execute.go +++ b/internal/app/enaptercli/execute.go @@ -5,9 +5,8 @@ import ( "bytes" "fmt" "io" + "io/fs" "os" - "path/filepath" - "strings" "github.com/urfave/cli/v2" ) @@ -32,11 +31,12 @@ func NewApp() *cli.App { } func zipDir(path string) ([]byte, error) { + fsys := os.DirFS(path) + buf := &bytes.Buffer{} zw := zip.NewWriter(buf) - path = filepath.Clean(path) - err := filepath.WalkDir(path, func(filePath string, entry os.DirEntry, err error) error { + err := fs.WalkDir(fsys, ".", func(path string, entry fs.DirEntry, err error) error { if err != nil { return err } @@ -44,22 +44,19 @@ func zipDir(path string) ([]byte, error) { return nil } - relPath := strings.TrimPrefix(filePath, path) - relPath = strings.TrimPrefix(relPath, "/") - zipFile, err := zw.Create(relPath) + f, err := fsys.Open(path) if err != nil { - return err + return fmt.Errorf("open: %w", err) } + defer f.Close() - fsFile, err := os.Open(filePath) + zf, err := zw.Create(path) if err != nil { - return err + return fmt.Errorf("create: %w", err) } - defer fsFile.Close() - _, err = io.Copy(zipFile, fsFile) - if err != nil { - return err + if _, err = io.Copy(zf, f); err != nil { + return fmt.Errorf("copy: %w", err) } return nil }) From 11e5f06f67619e9a8c1b2311f3a49ed2f9dc180c Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Thu, 11 Dec 2025 12:38:50 +0400 Subject: [PATCH 82/88] feat: add initial version of remote terminal --- go.mod | 5 +- go.sum | 4 + internal/app/enaptercli/cmd_base.go | 24 +- internal/app/enaptercli/cmd_device.go | 1 + .../app/enaptercli/cmd_device_run_terminal.go | 259 ++++++++++++++++++ .../enaptercli/testdata/helps/enapter device | 1 + .../helps/enapter device run-terminal | 21 ++ 7 files changed, 301 insertions(+), 14 deletions(-) create mode 100644 internal/app/enaptercli/cmd_device_run_terminal.go create mode 100644 internal/app/enaptercli/testdata/helps/enapter device run-terminal diff --git a/go.mod b/go.mod index d8f77cc..215c2f5 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ module github.com/enapter/enapter-cli -go 1.23.0 - +go 1.24.0 require ( github.com/gorilla/websocket v1.5.3 github.com/stretchr/testify v1.9.0 github.com/urfave/cli/v2 v2.27.4 + golang.org/x/term v0.37.0 ) require ( @@ -15,5 +15,6 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + golang.org/x/sys v0.38.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d960876..2e6ee48 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,10 @@ github.com/urfave/cli/v2 v2.27.4 h1:o1owoI+02Eb+K107p27wEX9Bb8eqIoZCfLXloLUSWJ8= github.com/urfave/cli/v2 v2.27.4/go.mod h1:m4QzxcD2qpra4z7WhzEGn74WZLViBnMpb1ToCAKdGRQ= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index af7e3e7..cc403ca 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -149,13 +149,23 @@ type runWebSocketParams struct { } func (c *cmdBase) runWebSocket(ctx context.Context, p runWebSocketParams) error { + url, err := url.Parse(c.apiHost + "/v3" + p.Path) + if err != nil { + return fmt.Errorf("parse url: %w", err) + } + url.RawQuery = p.Query.Encode() + + headers := make(http.Header) + headers.Set("X-Enapter-Auth-Token", c.token) + headers.Set("User-Agent", c.userAgent) + for retry := false; ; retry = true { if retry { fmt.Fprintln(c.errWriter, "Reconnecting...") time.Sleep(time.Second) } - conn, err := c.dialWebSocket(ctx, p.Path, p.Query) + conn, err := c.dialWebSocket(ctx, url, headers) if err != nil { if e := cli.ExitCoder(nil); errors.As(err, &e) { return err @@ -205,18 +215,8 @@ func (c *cmdBase) defaultRespProcessor(resp *http.Response) error { } func (c *cmdBase) dialWebSocket( - ctx context.Context, path string, query url.Values, + ctx context.Context, url *url.URL, headers http.Header, ) (*websocket.Conn, error) { - url, err := url.Parse(c.apiHost + "/v3" + path) - if err != nil { - return nil, fmt.Errorf("parse url: %w", err) - } - url.RawQuery = query.Encode() - - headers := make(http.Header) - headers.Set("X-Enapter-Auth-Token", c.token) - headers.Set("User-Agent", c.userAgent) - const timeout = 5 * time.Second dialer := websocket.Dialer{ HandshakeTimeout: timeout, diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index e41ae70..b07282a 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -29,6 +29,7 @@ func buildCmdDevice() *cli.Command { buildCmdDeviceCommand(), buildCmdDeviceTelemetry(), buildCmdDeviceCommunicationConfig(), + buildCmdDeviceRunTerminal(), }, } } diff --git a/internal/app/enaptercli/cmd_device_run_terminal.go b/internal/app/enaptercli/cmd_device_run_terminal.go new file mode 100644 index 0000000..4da2af7 --- /dev/null +++ b/internal/app/enaptercli/cmd_device_run_terminal.go @@ -0,0 +1,259 @@ +package enaptercli + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "os" + "time" + + "github.com/gorilla/websocket" + "github.com/urfave/cli/v2" + "golang.org/x/term" +) + +type cmdDeviceRunTerminal struct { + cmdDevice + deviceID string + winWidth int + winHeight int +} + +func buildCmdDeviceRunTerminal() *cli.Command { + cmd := &cmdDeviceRunTerminal{} + return &cli.Command{ + Name: "run-terminal", + Usage: "Run new remote terminal session", + Description: "Remote terminal feature should be enabled in gateway settings. " + + "Use Ctrl+] sequence to force connection close.", + CustomHelpTemplate: cmd.CommandHelpTemplate(), + Flags: cmd.Flags(), + Before: cmd.Before, + Action: func(cliCtx *cli.Context) error { + return cmd.do(cliCtx.Context) + }, + } +} + +func (c *cmdDeviceRunTerminal) Flags() []cli.Flag { + flags := c.cmdDevice.Flags() + return append(flags, &cli.StringFlag{ + Name: "device-id", + Aliases: []string{"d"}, + Usage: "gateway device ID", + Destination: &c.deviceID, + Required: true, + }) +} + +func (c *cmdDeviceRunTerminal) do(ctx context.Context) error { + fin := os.Stdin + fd := int(fin.Fd()) + if !term.IsTerminal(fd) { + return cli.Exit("Standard input should be a terminal.", 1) + } + + var credentials struct { + ChannelID string `json:"channel_id"` + Token string `json:"token"` + WebSocketURL string `json:"websocket_url"` + } + if err := c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodPost, + Path: "/" + c.deviceID + "/run_terminal", + RespProcessor: func(r *http.Response) error { + if r.StatusCode != http.StatusOK { + return cli.Exit(parseRespErrorMessage(r), 1) + } + return json.NewDecoder(r.Body).Decode(&credentials) + }, + }); err != nil { + return err + } + + url, err := url.Parse(credentials.WebSocketURL) + if err != nil { + return fmt.Errorf("parse url: %w", err) + } + headers := make(http.Header) + headers.Set("Authorization", "Bearer "+credentials.Token) + + conn, err := c.dialWebSocket(ctx, url, headers) + if err != nil { + return fmt.Errorf("dial websocket: %w", err) + } + defer conn.Close() + + oldState, err := term.MakeRaw(fd) + if err != nil { + return fmt.Errorf("make raw terminal: %w", err) + } + defer func() { _ = term.Restore(fd, oldState) }() + + // TODO: wait for pong? + if err := c.writePing(conn, credentials.ChannelID); err != nil { + return fmt.Errorf("ping: %w", err) + } + + fmt.Fprint(c.writer, "Use Ctrl+] to terminate the session.\r\n\r\n") + + errCh := make(chan error) + stdinCh := make(chan byte) + go func() { errCh <- c.runFileReader(ctx, fin, stdinCh) }() + go func() { errCh <- c.runConnReader(ctx, conn) }() + + return c.run(ctx, conn, credentials.ChannelID, stdinCh, errCh, fd) +} + +func (c *cmdDeviceRunTerminal) runFileReader( + ctx context.Context, f *os.File, ch chan<- byte, +) error { + for { + select { + case <-ctx.Done(): + return nil + default: + } + + var buf [1]byte + n, err := f.Read(buf[:]) + if err != nil { + return err + } + + if n > 0 { + select { + case <-ctx.Done(): + return nil + case ch <- buf[0]: + } + } + } +} + +func (c *cmdDeviceRunTerminal) runConnReader( + ctx context.Context, conn *websocket.Conn, +) error { + for { + select { + case <-ctx.Done(): + return nil + default: + } + + var msg struct { + Data string `json:"data"` + } + if err := conn.ReadJSON(&msg); err != nil { + return fmt.Errorf("read: %w", err) + } + + var data []string + if err := json.Unmarshal([]byte(msg.Data), &data); err != nil { + return fmt.Errorf("unmarhal data: %w", err) + } + + if len(data) == 0 { + return cli.Exit("Unexpected payload from server.", 1) + } + if data[0] == "exit" { + return nil + } + if data[0] == "stdin" && len(data) > 1 { + fmt.Fprint(c.writer, data[1]) + } + } +} + +func (c *cmdDeviceRunTerminal) run( + ctx context.Context, conn *websocket.Conn, channelID string, + stdinCh <-chan byte, errCh <-chan error, fd int, +) error { + const keepAliveInterval = 30 * time.Second + const updateSizeInterval = time.Second + + keepAliveTicker := time.NewTicker(keepAliveInterval) + updateSizeTicker := time.NewTicker(updateSizeInterval) + + for { + select { + case <-ctx.Done(): + return nil + case err := <-errCh: + return err + default: + } + + select { + case <-ctx.Done(): + return nil + case err := <-errCh: + return err + case <-keepAliveTicker.C: + if err := c.writeKeepalive(conn, channelID); err != nil { + return err + } + case <-updateSizeTicker.C: + if err := c.writeSetSize(conn, channelID, fd); err != nil { + return err + } + case b := <-stdinCh: + const GS = 29 // ^] + if b == GS { + return cli.Exit("Exiting session.", 0) + } + if err := c.writeStdin(conn, channelID, b); err != nil { + return err + } + } + } +} + +func (c *cmdDeviceRunTerminal) writePing(conn *websocket.Conn, channelID string) error { + return conn.WriteJSON(map[string]any{ + "channel": channelID, + "data": `["ping"]`, + }) +} + +func (c *cmdDeviceRunTerminal) writeStdin(conn *websocket.Conn, channelID string, b byte) error { + data, err := json.Marshal([]string{"stdin", string(b)}) + if err != nil { + return err + } + return conn.WriteJSON(map[string]any{ + "channel": channelID, + "data": string(data), + }) +} + +func (c *cmdDeviceRunTerminal) writeKeepalive(conn *websocket.Conn, channelID string) error { + return conn.WriteJSON(map[string]any{ + "channel": channelID, + "data": `["keepalive_ping"]`, + }) +} + +func (c *cmdDeviceRunTerminal) writeSetSize(conn *websocket.Conn, channelID string, fd int) error { + w, h, err := term.GetSize(fd) + if err != nil { + // FIXME: error on Windows + return nil + } + if c.winWidth == w && c.winHeight == h { + return nil + } + + if err := conn.WriteJSON(map[string]any{ + "channel": channelID, + "data": fmt.Sprintf("[%q, %d, %d]", "set_size", h, w), + }); err != nil { + return err + } + + c.winWidth = w + c.winHeight = h + return nil +} diff --git a/internal/app/enaptercli/testdata/helps/enapter device b/internal/app/enaptercli/testdata/helps/enapter device index 0ba3641..131a4c7 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device +++ b/internal/app/enaptercli/testdata/helps/enapter device @@ -15,6 +15,7 @@ COMMANDS: command Manage device commands telemetry Show device telemetry communication-config Manage device communication config + run-terminal Run new remote terminal session help, h Shows a list of commands or help for one command OPTIONS: diff --git a/internal/app/enaptercli/testdata/helps/enapter device run-terminal b/internal/app/enaptercli/testdata/helps/enapter device run-terminal new file mode 100644 index 0000000..b6a777b --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter device run-terminal @@ -0,0 +1,21 @@ +NAME: + enaptercli.test device run-terminal - Run new remote terminal session + +USAGE: + enaptercli.test device run-terminal [command options] + +DESCRIPTION: + Remote terminal feature should be enabled in gateway settings. Use Ctrl+] sequence to force connection close. + +OPTIONS: + --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --device-id value, -d value gateway device ID + --help, -h show help + +ENVIRONMENT VARIABLES: + ENAPTER3_API_TOKEN Enapter API access token + ENAPTER3_API_URL Enapter API base URL (default: https://api.enapter.com) + ENAPTER3_API_ALLOW_INSECURE Allow insecure connections to the Enapter API (default: false) + From a8f947e2f21e098c587d6dcd44735686808f878f Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Tue, 23 Dec 2025 15:28:08 +0400 Subject: [PATCH 83/88] feat: add connection management --- internal/app/configfile/config.go | 84 ++++++++++ internal/app/enaptercli/cmd_base.go | 78 +++++++-- internal/app/enaptercli/cmd_connection.go | 18 +++ internal/app/enaptercli/cmd_connection_add.go | 150 ++++++++++++++++++ .../app/enaptercli/cmd_connection_list.go | 60 +++++++ .../app/enaptercli/cmd_connection_remove.go | 50 ++++++ .../enaptercli/cmd_connection_set_default.go | 42 +++++ internal/app/enaptercli/errors.go | 2 - internal/app/enaptercli/execute.go | 1 + .../app/enaptercli/testdata/helps/enapter | 1 + .../testdata/helps/enapter blueprint download | 2 +- .../testdata/helps/enapter blueprint get | 2 +- .../helps/enapter blueprint profiles download | 10 +- .../helps/enapter blueprint profiles upload | 10 +- .../testdata/helps/enapter blueprint upload | 10 +- .../testdata/helps/enapter connection | 15 ++ .../testdata/helps/enapter connection add | 14 ++ .../testdata/helps/enapter connection list | 8 + .../testdata/helps/enapter connection remove | 9 ++ .../helps/enapter connection set-default | 9 ++ .../helps/enapter device change-blueprint | 2 +- .../helps/enapter device command execute | 14 +- .../testdata/helps/enapter device command get | 2 +- .../helps/enapter device command list | 10 +- ...apter device communication-config generate | 12 +- .../helps/enapter device create lua-device | 2 +- .../helps/enapter device create standalone | 2 +- .../testdata/helps/enapter device delete | 10 +- .../testdata/helps/enapter device get | 2 +- .../testdata/helps/enapter device list | 2 +- .../testdata/helps/enapter device logs | 26 +-- .../helps/enapter device run-terminal | 10 +- .../testdata/helps/enapter device telemetry | 12 +- .../testdata/helps/enapter device update | 14 +- .../testdata/helps/enapter rule-engine get | 8 +- .../testdata/helps/enapter rule-engine resume | 8 +- .../helps/enapter rule-engine rule create | 18 +-- .../helps/enapter rule-engine rule delete | 10 +- .../helps/enapter rule-engine rule disable | 2 +- .../helps/enapter rule-engine rule enable | 2 +- .../helps/enapter rule-engine rule get | 10 +- .../helps/enapter rule-engine rule list | 8 +- .../helps/enapter rule-engine rule logs | 12 +- .../helps/enapter rule-engine rule update | 12 +- .../enapter rule-engine rule update-script | 16 +- .../helps/enapter rule-engine suspend | 8 +- .../testdata/helps/enapter site get | 10 +- .../testdata/helps/enapter site list | 12 +- 48 files changed, 674 insertions(+), 157 deletions(-) create mode 100644 internal/app/configfile/config.go create mode 100644 internal/app/enaptercli/cmd_connection.go create mode 100644 internal/app/enaptercli/cmd_connection_add.go create mode 100644 internal/app/enaptercli/cmd_connection_list.go create mode 100644 internal/app/enaptercli/cmd_connection_remove.go create mode 100644 internal/app/enaptercli/cmd_connection_set_default.go create mode 100644 internal/app/enaptercli/testdata/helps/enapter connection create mode 100644 internal/app/enaptercli/testdata/helps/enapter connection add create mode 100644 internal/app/enaptercli/testdata/helps/enapter connection list create mode 100644 internal/app/enaptercli/testdata/helps/enapter connection remove create mode 100644 internal/app/enaptercli/testdata/helps/enapter connection set-default diff --git a/internal/app/configfile/config.go b/internal/app/configfile/config.go new file mode 100644 index 0000000..fa1e7f1 --- /dev/null +++ b/internal/app/configfile/config.go @@ -0,0 +1,84 @@ +package configfile + +import ( + "encoding/json" + "errors" + "fmt" + "io/fs" + "os" + "path/filepath" +) + +type Config struct { + DefaultConn string `json:"default_connection,omitempty"` + Connections map[string]Connection `json:"connections,omitempty"` +} + +type Connection struct { + Gateway bool `json:"gateway,omitempty"` + URL string `json:"url"` + SiteID string `json:"site_id,omitempty"` + Token Token `json:"token"` + AllowInsecure bool `json:"allow_insecure,omitempty"` +} + +type Token struct { + Value string `json:"value"` +} + +const ( + dirName = ".enapter3" + fileName = "config.json" +) + +func Load() (Config, error) { + home, err := os.UserHomeDir() + if err != nil { + return Config{}, fmt.Errorf("get home dir: %w", err) + } + + path := filepath.Join(home, dirName, fileName) + f, err := os.Open(path) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + return Config{}, nil + } + return Config{}, fmt.Errorf("open config file: %w", err) + } + defer f.Close() + + var config Config + if err := json.NewDecoder(f).Decode(&config); err != nil { + return Config{}, fmt.Errorf("decode config file: %w", err) + } + + return config, nil +} + +func Save(c Config) error { + home, err := os.UserHomeDir() + if err != nil { + return fmt.Errorf("get home dir: %w", err) + } + + const perm = 0o755 + dir := filepath.Join(home, dirName) + if err := os.MkdirAll(dir, perm); err != nil { + return fmt.Errorf("create config dir: %w", err) + } + + path := filepath.Join(home, dirName, fileName) + f, err := os.Create(path) + if err != nil { + return fmt.Errorf("create config file: %w", err) + } + defer f.Close() + + encoder := json.NewEncoder(f) + encoder.SetIndent("", " ") + if err := encoder.Encode(c); err != nil { + return fmt.Errorf("encode config file: %w", err) + } + + return f.Sync() +} diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index cc403ca..e588501 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -17,14 +17,20 @@ import ( "github.com/gorilla/websocket" "github.com/urfave/cli/v2" + + "github.com/enapter/enapter-cli/internal/app/configfile" ) +const defaultURL = "https://api.enapter.com" + type cmdBase struct { - verbose bool + connName string token string - userAgent string - apiHost string + apiURL string + siteID string apiAllowInsecure bool + verbose bool + userAgent string writer io.Writer errWriter io.Writer httpClient *http.Client @@ -32,6 +38,12 @@ type cmdBase struct { func (c *cmdBase) Flags() []cli.Flag { return []cli.Flag{ + &cli.StringFlag{ + Name: "connection", + Usage: "name of the connection to use", + Aliases: []string{"c"}, + Destination: &c.connName, + }, &cli.StringFlag{ Name: "token", Usage: "Enapter API token", @@ -43,10 +55,11 @@ func (c *cmdBase) Flags() []cli.Flag { Name: "api-url", Usage: "override API base URL", EnvVars: []string{"ENAPTER3_API_URL"}, - Value: "https://api.enapter.com", - Destination: &c.apiHost, + Value: defaultURL, + Hidden: true, + Destination: &c.apiURL, Action: func(_ *cli.Context, v string) error { - c.apiHost = strings.TrimSuffix(v, "/") + c.apiURL = strings.TrimSuffix(v, "/") return nil }, }, @@ -65,9 +78,10 @@ func (c *cmdBase) Flags() []cli.Flag { } func (c *cmdBase) Before(cliCtx *cli.Context) error { - if cliCtx.String("token") == "" { - return errAPITokenMissed + if err := c.setupCredentials(cliCtx); err != nil { + return err } + c.userAgent = "enapter-cli/" + cliCtx.App.Version c.writer = cliCtx.App.Writer c.errWriter = cliCtx.App.ErrWriter @@ -81,6 +95,50 @@ func (c *cmdBase) Before(cliCtx *cli.Context) error { return nil } +func (c *cmdBase) setupCredentials(cliCtx *cli.Context) error { + config, err := configfile.Load() + if err != nil { + return err + } + + if c.connName != "" { + conn, ok := config.Connections[c.connName] + if !ok { + return cli.Exit("Unknown connection name.", 1) + } + if cliCtx.IsSet("token") || cliCtx.IsSet("api-url") || cliCtx.IsSet("api-allow-insecure") { + fmt.Fprintln(cliCtx.App.ErrWriter, + "WARNING: credentials set via environment variables or flags are ignored.") + } + c.token = conn.Token.Value + c.apiURL = conn.URL + c.siteID = conn.SiteID + c.apiAllowInsecure = conn.AllowInsecure + return nil + } + + if c.token != "" { + return nil + } + + if config.DefaultConn != "" { + conn, ok := config.Connections[config.DefaultConn] + if !ok { + return cli.Exit("Default connection is invalid.", 1) + } + c.token = conn.Token.Value + c.apiURL = conn.URL + c.siteID = conn.SiteID + c.apiAllowInsecure = conn.AllowInsecure + return nil + } + + return cli.Exit("No connection configured.\n\n"+ + "Please, specify connection using --connection flag.\n\n"+ + "To list available connections:\n$ enapter3 connection list\n\n"+ + "To add a new connection:\n$ enapter3 connection add\n", 1) +} + const enapterAPIEnvVarsHelp = ` ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token @@ -107,7 +165,7 @@ type doHTTPRequestParams struct { } func (c *cmdBase) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { - req, err := http.NewRequestWithContext(ctx, p.Method, c.apiHost+"/v3"+p.Path, p.Body) + req, err := http.NewRequestWithContext(ctx, p.Method, c.apiURL+"/v3"+p.Path, p.Body) if err != nil { return fmt.Errorf("build http request: %w", err) } @@ -149,7 +207,7 @@ type runWebSocketParams struct { } func (c *cmdBase) runWebSocket(ctx context.Context, p runWebSocketParams) error { - url, err := url.Parse(c.apiHost + "/v3" + p.Path) + url, err := url.Parse(c.apiURL + "/v3" + p.Path) if err != nil { return fmt.Errorf("parse url: %w", err) } diff --git a/internal/app/enaptercli/cmd_connection.go b/internal/app/enaptercli/cmd_connection.go new file mode 100644 index 0000000..e8d91d8 --- /dev/null +++ b/internal/app/enaptercli/cmd_connection.go @@ -0,0 +1,18 @@ +package enaptercli + +import ( + "github.com/urfave/cli/v2" +) + +func buildCmdConnection() *cli.Command { + return &cli.Command{ + Name: "connection", + Usage: "Manage connections to Enapter Cloud and Gateways", + Subcommands: []*cli.Command{ + buildCmdConnectionAdd(), + buildCmdConnectionRemove(), + buildCmdConnectionList(), + buildCmdConnectionSetDefault(), + }, + } +} diff --git a/internal/app/enaptercli/cmd_connection_add.go b/internal/app/enaptercli/cmd_connection_add.go new file mode 100644 index 0000000..f962682 --- /dev/null +++ b/internal/app/enaptercli/cmd_connection_add.go @@ -0,0 +1,150 @@ +package enaptercli + +import ( + "crypto/tls" + "encoding/json" + "fmt" + "net/http" + "net/url" + + "github.com/urfave/cli/v2" + + "github.com/enapter/enapter-cli/internal/app/configfile" +) + +type cmdConnectionAdd struct { + name string + url string + token string + siteID string + gateway bool + allowInsecure bool +} + +func buildCmdConnectionAdd() *cli.Command { + cmd := &cmdConnectionAdd{} + return &cli.Command{ + Name: "add", + Usage: "Add a new connection", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Usage: "connection name", + Destination: &cmd.name, + Required: true, + }, + &cli.BoolFlag{ + Name: "gateway", + Usage: "indicates that the connection is to a Gateway", + Destination: &cmd.gateway, + }, + &cli.StringFlag{ + Name: "url", + Usage: "Enapter API base URL", + Destination: &cmd.url, + Value: defaultURL, + }, + &cli.StringFlag{ + Name: "token", + Usage: "Enapter API access token", + Destination: &cmd.token, + Required: true, + }, + &cli.StringFlag{ + Name: "site-id", + Usage: "if specified, the connection will be limited to this site " + + "(available only for Cloud connections)", + Destination: &cmd.siteID, + }, + &cli.BoolFlag{ + Name: "allow-insecure", + Usage: "allow insecure connections to the Enapter API", + Destination: &cmd.allowInsecure, + }, + }, + Action: cmd.do, + } +} + +func (c *cmdConnectionAdd) do(cliCtx *cli.Context) error { + config, err := configfile.Load() + if err != nil { + return err + } + + if _, exists := config.Connections[c.name]; exists { + return cli.Exit("Connection with the given name already exists.", 1) + } + + if u, err := url.Parse(c.url); err != nil { + return cli.Exit("Invalid URL format: "+err.Error()+".", 1) + } else if u.Scheme != "https" && u.Scheme != "http" { + return cli.Exit("URL scheme must be http or https.", 1) + } + + if c.gateway { + if c.url == defaultURL { + return cli.Exit("Gateway connections require a custom URL.", 1) + } + if c.siteID != "" { + return cli.Exit("The site-id option cannot be used with gateway connections.", 1) + } + siteID, err := c.resolveGatewaySiteID(cliCtx) + if err != nil { + return err + } + c.siteID = siteID + } + + if config.Connections == nil { + config.Connections = make(map[string]configfile.Connection) + } + config.Connections[c.name] = configfile.Connection{ + Gateway: c.gateway, + URL: c.url, + SiteID: c.siteID, + Token: configfile.Token{Value: c.token}, + AllowInsecure: c.allowInsecure, + } + + return configfile.Save(config) +} + +func (c *cmdConnectionAdd) resolveGatewaySiteID(cliCtx *cli.Context) (string, error) { + client := &http.Client{ + Transport: &http.Transport{ + //nolint:gosec // This is needed to allow self-signed certificates on Gateway. + TLSClientConfig: &tls.Config{InsecureSkipVerify: c.allowInsecure}, + }, + } + + req, err := http.NewRequestWithContext( + cliCtx.Context, http.MethodGet, c.url+"/v3/site", nil) + if err != nil { + return "", fmt.Errorf("new http request: %w", err) + } + + req.Header.Set("X-Enapter-Auth-Token", c.token) + req.Header.Set("User-Agent", "enapter-cli/"+cliCtx.App.Version) + + resp, err := client.Do(req) + if err != nil { + return "", fmt.Errorf("send http request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return "", cli.Exit("Unexpected response from Gateway: "+resp.Status+". ", 1) + } + + var siteResp struct { + Site struct { + ID string `json:"id"` + } `json:"site"` + } + if err := json.NewDecoder(resp.Body).Decode(&siteResp); err != nil { + return "", fmt.Errorf("decode http response: %w", err) + } + + return siteResp.Site.ID, nil +} diff --git a/internal/app/enaptercli/cmd_connection_list.go b/internal/app/enaptercli/cmd_connection_list.go new file mode 100644 index 0000000..90a0cd7 --- /dev/null +++ b/internal/app/enaptercli/cmd_connection_list.go @@ -0,0 +1,60 @@ +package enaptercli + +import ( + "fmt" + "maps" + "slices" + "text/tabwriter" + + "github.com/urfave/cli/v2" + + "github.com/enapter/enapter-cli/internal/app/configfile" +) + +type cmdConnectionList struct{} + +func buildCmdConnectionList() *cli.Command { + cmd := &cmdConnectionList{} + return &cli.Command{ + Name: "list", + Usage: "List all connections", + Action: cmd.do, + } +} + +func (c *cmdConnectionList) do(cliCtx *cli.Context) error { + config, err := configfile.Load() + if err != nil { + return err + } + + const padding = 3 + w := tabwriter.NewWriter(cliCtx.App.Writer, 0, 0, padding, ' ', 0) + + fmt.Fprintln(w, "NAME\tTYPE\tURL\tALLOW INSECURE\tSITE ID") + + names := slices.Sorted(maps.Keys(config.Connections)) + for _, name := range names { + conn := config.Connections[name] + + displayName := name + if name == config.DefaultConn { + displayName += " *" + } + + typ := "cloud" + if conn.Gateway { + typ = "gateway" + } + + allowInsecure := "no" + if conn.AllowInsecure { + allowInsecure = "yes" + } + + fmt.Fprintf(w, "%s\t%s\t%v\t%v\t%v\n", + displayName, typ, conn.URL, allowInsecure, conn.SiteID) + } + + return w.Flush() +} diff --git a/internal/app/enaptercli/cmd_connection_remove.go b/internal/app/enaptercli/cmd_connection_remove.go new file mode 100644 index 0000000..55bd849 --- /dev/null +++ b/internal/app/enaptercli/cmd_connection_remove.go @@ -0,0 +1,50 @@ +package enaptercli + +import ( + "fmt" + + "github.com/urfave/cli/v2" + + "github.com/enapter/enapter-cli/internal/app/configfile" +) + +type cmdConnectionRemove struct { + name string +} + +func buildCmdConnectionRemove() *cli.Command { + cmd := &cmdConnectionRemove{} + return &cli.Command{ + Name: "remove", + Usage: "Remove a connection", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Usage: "connection name", + Destination: &cmd.name, + Required: true, + }, + }, + Action: cmd.do, + } +} + +func (c *cmdConnectionRemove) do(cliCtx *cli.Context) error { + config, err := configfile.Load() + if err != nil { + return err + } + + if _, ok := config.Connections[c.name]; !ok { + fmt.Fprintln(cliCtx.App.ErrWriter, "WARNING: unknown connection.") + return nil + } + + delete(config.Connections, c.name) + if config.DefaultConn == c.name { + fmt.Fprintln(cliCtx.App.ErrWriter, "WARNING: removed connection was set as default.") + config.DefaultConn = "" + } + + return configfile.Save(config) +} diff --git a/internal/app/enaptercli/cmd_connection_set_default.go b/internal/app/enaptercli/cmd_connection_set_default.go new file mode 100644 index 0000000..403ed4f --- /dev/null +++ b/internal/app/enaptercli/cmd_connection_set_default.go @@ -0,0 +1,42 @@ +package enaptercli + +import ( + "github.com/urfave/cli/v2" + + "github.com/enapter/enapter-cli/internal/app/configfile" +) + +type cmdConnectionSetDefault struct { + name string +} + +func buildCmdConnectionSetDefault() *cli.Command { + cmd := &cmdConnectionSetDefault{} + return &cli.Command{ + Name: "set-default", + Usage: "Set default connection", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "name", + Usage: "connection name", + Destination: &cmd.name, + Required: true, + }, + }, + Action: cmd.do, + } +} + +func (c *cmdConnectionSetDefault) do(*cli.Context) error { + config, err := configfile.Load() + if err != nil { + return err + } + + if _, ok := config.Connections[c.name]; !ok { + return cli.Exit("Unknown connection.", 1) + } + + config.DefaultConn = c.name + return configfile.Save(config) +} diff --git a/internal/app/enaptercli/errors.go b/internal/app/enaptercli/errors.go index b1e45a7..0a4901a 100644 --- a/internal/app/enaptercli/errors.go +++ b/internal/app/enaptercli/errors.go @@ -3,8 +3,6 @@ package enaptercli import "errors" var ( - errAPITokenMissed = errors.New("API token missing. Set it up using environment " + - "variable ENAPTER3_API_TOKEN") errUnsupportedFlagValue = errors.New("unsupported flag value") errOnlyOneBlueprinFlag = errors.New("only one of --blueprint-id or --blueprint-path can be specified") errMissedBlueprintFlag = errors.New("one of --blueprint-id or --blueprint-path must be specified") diff --git a/internal/app/enaptercli/execute.go b/internal/app/enaptercli/execute.go index 0f13862..8010ce4 100644 --- a/internal/app/enaptercli/execute.go +++ b/internal/app/enaptercli/execute.go @@ -25,6 +25,7 @@ func NewApp() *cli.App { buildCmdDevice(), buildCmdBlueprint(), buildCmdRuleEngine(), + buildCmdConnection(), } return app diff --git a/internal/app/enaptercli/testdata/helps/enapter b/internal/app/enaptercli/testdata/helps/enapter index 9f058d5..159cf84 100644 --- a/internal/app/enaptercli/testdata/helps/enapter +++ b/internal/app/enaptercli/testdata/helps/enapter @@ -12,6 +12,7 @@ COMMANDS: device Manage devices blueprint Manage blueprints rule-engine Manage the rule engine + connection Manage connections to Enapter Cloud and Gateways help, h Shows a list of commands or help for one command GLOBAL OPTIONS: diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint download b/internal/app/enaptercli/testdata/helps/enapter blueprint download index d59236d..7081ba2 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint download @@ -5,7 +5,7 @@ USAGE: enaptercli.test blueprint download [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --blueprint-id value, -b value blueprint name or ID to download diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint get b/internal/app/enaptercli/testdata/helps/enapter blueprint get index 6dfa405..b104785 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint get +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint get @@ -5,7 +5,7 @@ USAGE: enaptercli.test blueprint get [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --blueprint-id value, -b value blueprint name or ID to retrieve diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download index 1342fdb..88a0bfe 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles download @@ -5,11 +5,11 @@ USAGE: enaptercli.test blueprint profiles download [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --output value, -o value file name to save the downloaded profiles - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --output value, -o value file name to save the downloaded profiles + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload index c3ac04e..1d5da02 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint profiles upload @@ -5,11 +5,11 @@ USAGE: enaptercli.test blueprint profiles upload [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --path value, -p value profiles zip file path - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --path value, -p value profiles zip file path + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter blueprint upload b/internal/app/enaptercli/testdata/helps/enapter blueprint upload index e6af592..7932fb2 100644 --- a/internal/app/enaptercli/testdata/helps/enapter blueprint upload +++ b/internal/app/enaptercli/testdata/helps/enapter blueprint upload @@ -5,11 +5,11 @@ USAGE: enaptercli.test blueprint upload [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --path value, -p value blueprint path (zip file or directory) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --path value, -p value blueprint path (zip file or directory) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter connection b/internal/app/enaptercli/testdata/helps/enapter connection new file mode 100644 index 0000000..8438469 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter connection @@ -0,0 +1,15 @@ +NAME: + enaptercli.test connection - Manage connections to Enapter Cloud and Gateways + +USAGE: + enaptercli.test connection command [command options] + +COMMANDS: + add Add a new connection + remove Remove a connection + list List all connections + set-default Set default connection + help, h Shows a list of commands or help for one command + +OPTIONS: + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter connection add b/internal/app/enaptercli/testdata/helps/enapter connection add new file mode 100644 index 0000000..9ebed3e --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter connection add @@ -0,0 +1,14 @@ +NAME: + enaptercli.test connection add - Add a new connection + +USAGE: + enaptercli.test connection add [command options] + +OPTIONS: + --name value connection name + --gateway indicates that the connection is to a Gateway (default: false) + --url value Enapter API base URL (default: "https://api.enapter.com") + --token value Enapter API access token + --site-id value if specified, the connection will be limited to this site (available only for Cloud connections) + --allow-insecure allow insecure connections to the Enapter API (default: false) + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter connection list b/internal/app/enaptercli/testdata/helps/enapter connection list new file mode 100644 index 0000000..84afb87 --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter connection list @@ -0,0 +1,8 @@ +NAME: + enaptercli.test connection list - List all connections + +USAGE: + enaptercli.test connection list [command options] + +OPTIONS: + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter connection remove b/internal/app/enaptercli/testdata/helps/enapter connection remove new file mode 100644 index 0000000..207770c --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter connection remove @@ -0,0 +1,9 @@ +NAME: + enaptercli.test connection remove - Remove a connection + +USAGE: + enaptercli.test connection remove [command options] + +OPTIONS: + --name value connection name + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter connection set-default b/internal/app/enaptercli/testdata/helps/enapter connection set-default new file mode 100644 index 0000000..eb243ae --- /dev/null +++ b/internal/app/enaptercli/testdata/helps/enapter connection set-default @@ -0,0 +1,9 @@ +NAME: + enaptercli.test connection set-default - Set default connection + +USAGE: + enaptercli.test connection set-default [command options] + +OPTIONS: + --name value connection name + --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter device change-blueprint b/internal/app/enaptercli/testdata/helps/enapter device change-blueprint index d4591a0..450f917 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device change-blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter device change-blueprint @@ -5,7 +5,7 @@ USAGE: enaptercli.test device change-blueprint [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --device-id value, -d value device ID diff --git a/internal/app/enaptercli/testdata/helps/enapter device command execute b/internal/app/enaptercli/testdata/helps/enapter device command execute index b23a949..665ab2e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command execute +++ b/internal/app/enaptercli/testdata/helps/enapter device command execute @@ -5,13 +5,13 @@ USAGE: enaptercli.test device command execute [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --device-id value, -d value device ID - --name value command name - --arguments value command arguments (should be a JSON string) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --device-id value, -d value device ID + --name value command name + --arguments value command arguments (should be a JSON string) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter device command get b/internal/app/enaptercli/testdata/helps/enapter device command get index 8926ecd..8b1ea7c 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command get +++ b/internal/app/enaptercli/testdata/helps/enapter device command get @@ -5,7 +5,7 @@ USAGE: enaptercli.test device command get [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --device-id value, -d value device ID diff --git a/internal/app/enaptercli/testdata/helps/enapter device command list b/internal/app/enaptercli/testdata/helps/enapter device command list index af59597..c0c0b33 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command list +++ b/internal/app/enaptercli/testdata/helps/enapter device command list @@ -5,11 +5,11 @@ USAGE: enaptercli.test device command list [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --device-id value, -d value device ID - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --device-id value, -d value device ID + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter device communication-config generate b/internal/app/enaptercli/testdata/helps/enapter device communication-config generate index 8086f54..7867f2e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device communication-config generate +++ b/internal/app/enaptercli/testdata/helps/enapter device communication-config generate @@ -5,12 +5,12 @@ USAGE: enaptercli.test device communication-config generate [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --device-id value, -d value device ID - --protocol value connection protocol (supported values: MQTT, MQTTS) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --device-id value, -d value device ID + --protocol value connection protocol (supported values: MQTT, MQTTS) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter device create lua-device b/internal/app/enaptercli/testdata/helps/enapter device create lua-device index a082ac1..8e634b2 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device create lua-device +++ b/internal/app/enaptercli/testdata/helps/enapter device create lua-device @@ -5,7 +5,7 @@ USAGE: enaptercli.test device create lua-device [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --runtime-id value, -r value UCM device ID where the new Lua device will run diff --git a/internal/app/enaptercli/testdata/helps/enapter device create standalone b/internal/app/enaptercli/testdata/helps/enapter device create standalone index b8e9f0e..f98d0f1 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device create standalone +++ b/internal/app/enaptercli/testdata/helps/enapter device create standalone @@ -5,7 +5,7 @@ USAGE: enaptercli.test device create standalone [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --site-id value, -s value site ID where the device will be created diff --git a/internal/app/enaptercli/testdata/helps/enapter device delete b/internal/app/enaptercli/testdata/helps/enapter device delete index f14a7a5..8cc1577 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device delete +++ b/internal/app/enaptercli/testdata/helps/enapter device delete @@ -5,11 +5,11 @@ USAGE: enaptercli.test device delete [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --device-id value, -d value device ID - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --device-id value, -d value device ID + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter device get b/internal/app/enaptercli/testdata/helps/enapter device get index 0c7a463..afdc0a3 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device get +++ b/internal/app/enaptercli/testdata/helps/enapter device get @@ -5,7 +5,7 @@ USAGE: enaptercli.test device get [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --device-id value, -d value device ID diff --git a/internal/app/enaptercli/testdata/helps/enapter device list b/internal/app/enaptercli/testdata/helps/enapter device list index b7370ed..5725d68 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device list +++ b/internal/app/enaptercli/testdata/helps/enapter device list @@ -5,7 +5,7 @@ USAGE: enaptercli.test device list [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --expand value [ --expand value ] coma-separated list of expanded device information (supported values: connectivity, manifest, properties, communication, site) diff --git a/internal/app/enaptercli/testdata/helps/enapter device logs b/internal/app/enaptercli/testdata/helps/enapter device logs index 717d7f3..e276c97 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device logs +++ b/internal/app/enaptercli/testdata/helps/enapter device logs @@ -5,19 +5,19 @@ USAGE: enaptercli.test device logs [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --device-id value, -d value device ID - --follow, -f follow the log output (default: false) - --from value from timestamp in RFC 3339 format (e.g. 2006-01-02T15:04:05Z) - --to value to timestamp in RFC 3339 format (e.g. 2006-01-02T15:04:05Z) - --limit value, -l value maximum number of logs to retrieve (default: 0) - --offset value, -o value number of logs to skip when retrieving (default: 0) - --severity value, -s value filter logs by severity - --order value order logs by criteria (RECEIVED_AT_ASC[default], RECEIVED_AT_DESC) - --show value filter logs by criteria (ALL[default], PERSISTED_ONLY, TEMPORARY_ONLY) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --device-id value, -d value device ID + --follow, -f follow the log output (default: false) + --from value from timestamp in RFC 3339 format (e.g. 2006-01-02T15:04:05Z) + --to value to timestamp in RFC 3339 format (e.g. 2006-01-02T15:04:05Z) + --limit value, -l value maximum number of logs to retrieve (default: 0) + --offset value, -o value number of logs to skip when retrieving (default: 0) + --severity value, -s value filter logs by severity + --order value order logs by criteria (RECEIVED_AT_ASC[default], RECEIVED_AT_DESC) + --show value filter logs by criteria (ALL[default], PERSISTED_ONLY, TEMPORARY_ONLY) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter device run-terminal b/internal/app/enaptercli/testdata/helps/enapter device run-terminal index b6a777b..6eb940e 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device run-terminal +++ b/internal/app/enaptercli/testdata/helps/enapter device run-terminal @@ -8,11 +8,11 @@ DESCRIPTION: Remote terminal feature should be enabled in gateway settings. Use Ctrl+] sequence to force connection close. OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --device-id value, -d value gateway device ID - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --device-id value, -d value gateway device ID + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter device telemetry b/internal/app/enaptercli/testdata/helps/enapter device telemetry index 7e52736..a379f9c 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device telemetry +++ b/internal/app/enaptercli/testdata/helps/enapter device telemetry @@ -5,12 +5,12 @@ USAGE: enaptercli.test device telemetry [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --device-id value, -d value device ID - --follow, -f follow the telemetry output (default: false) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --device-id value, -d value device ID + --follow, -f follow the telemetry output (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter device update b/internal/app/enaptercli/testdata/helps/enapter device update index 7959280..3a8dfce 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device update +++ b/internal/app/enaptercli/testdata/helps/enapter device update @@ -5,13 +5,13 @@ USAGE: enaptercli.test device update [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --device-id value, -d value device ID - --name value device name - --slug value device slug - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --device-id value, -d value device ID + --name value device name + --slug value device slug + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine get b/internal/app/enaptercli/testdata/helps/enapter rule-engine get index 4fb579f..b44c8e2 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine get +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine get @@ -5,10 +5,10 @@ USAGE: enaptercli.test rule-engine get [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume index c0a9858..5953592 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume @@ -5,10 +5,10 @@ USAGE: enaptercli.test rule-engine resume [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create index b2ab0c1..a0a7ace 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create @@ -5,15 +5,15 @@ USAGE: enaptercli.test rule-engine rule create [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --slug value Slug for the new rule - --script value Path to the file containing the script code - --runtime-version value Version of the runtime to use for the script execution (default: "V3") - --exec-interval value How frequently to execute the script (compatible only with runtime version 1) in duration format (e.g., 5s, 2m) - --disable Disable the rule upon creation (default: false) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --slug value Slug for the new rule + --script value Path to the file containing the script code + --runtime-version value Version of the runtime to use for the script execution (default: "V3") + --exec-interval value How frequently to execute the script (compatible only with runtime version 1) in duration format (e.g., 5s, 2m) + --disable Disable the rule upon creation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete index f7dfcc3..9d98846 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete @@ -5,11 +5,11 @@ USAGE: enaptercli.test rule-engine rule delete [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --rule-id value Rule ID or slug - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --rule-id value Rule ID or slug + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable index c6eaa23..217c3e6 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable @@ -5,7 +5,7 @@ USAGE: enaptercli.test rule-engine rule disable [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable index 20e3125..1419272 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable @@ -5,7 +5,7 @@ USAGE: enaptercli.test rule-engine rule enable [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] + --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) --rule-id value [ --rule-id value ] Rule IDs or slugs diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get index b590be4..41dd54d 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get @@ -5,11 +5,11 @@ USAGE: enaptercli.test rule-engine rule get [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --rule-id value Rule ID or slug - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --rule-id value Rule ID or slug + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list index 7804fa4..3cd3f96 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list @@ -5,10 +5,10 @@ USAGE: enaptercli.test rule-engine rule list [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs index a23a2a1..71efdf8 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs @@ -5,12 +5,12 @@ USAGE: enaptercli.test rule-engine rule logs [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --rule-id value rule ID - --follow, -f follow the log output (default: false) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --rule-id value rule ID + --follow, -f follow the log output (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update index 2979698..c9e11f9 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update @@ -5,12 +5,12 @@ USAGE: enaptercli.test rule-engine rule update [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --rule-id value Rule ID or slug to update - --slug value A new rule slug - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --rule-id value Rule ID or slug to update + --slug value A new rule slug + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script index c5355c0..0f40b58 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script @@ -5,14 +5,14 @@ USAGE: enaptercli.test rule-engine rule update-script [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --rule-id value Rule ID or slug to update - --script value Path to a file containing the script code - --runtime-version value Version of the runtime to use for the script execution (default: "V3") - --exec-interval value How frequently to execute the script (compatible only with runtime version 1) in duration format (e.g., 5s, 2m) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --rule-id value Rule ID or slug to update + --script value Path to a file containing the script code + --runtime-version value Version of the runtime to use for the script execution (default: "V3") + --exec-interval value How frequently to execute the script (compatible only with runtime version 1) in duration format (e.g., 5s, 2m) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend index 60e5168..3a50202 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend @@ -5,10 +5,10 @@ USAGE: enaptercli.test rule-engine suspend [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter site get b/internal/app/enaptercli/testdata/helps/enapter site get index 6c683f6..0d75156 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site get +++ b/internal/app/enaptercli/testdata/helps/enapter site get @@ -5,11 +5,11 @@ USAGE: enaptercli.test site get [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --site-id value site ID - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --site-id value site ID + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token diff --git a/internal/app/enaptercli/testdata/helps/enapter site list b/internal/app/enaptercli/testdata/helps/enapter site list index e95d0fe..0ce7752 100644 --- a/internal/app/enaptercli/testdata/helps/enapter site list +++ b/internal/app/enaptercli/testdata/helps/enapter site list @@ -5,12 +5,12 @@ USAGE: enaptercli.test site list [command options] OPTIONS: - --api-url value override API base URL (default: "https://api.enapter.com") [$ENAPTER3_API_URL] - --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] - --verbose log extra details about the operation (default: false) - --my-sites returns only sites where user is owner or installer (default: false) - --limit value maximum number of sites to retrieve (default: retrieves all) - --help, -h show help + --connection value, -c value name of the connection to use + --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] + --verbose log extra details about the operation (default: false) + --my-sites returns only sites where user is owner or installer (default: false) + --limit value maximum number of sites to retrieve (default: retrieves all) + --help, -h show help ENVIRONMENT VARIABLES: ENAPTER3_API_TOKEN Enapter API access token From 51b454daed84f443186b1c032805fcb33d9e4eef Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Wed, 24 Dec 2025 20:38:03 +0400 Subject: [PATCH 84/88] feat: use associated site-id for site commands --- internal/app/enaptercli/cmd_site_get.go | 13 ++++++++++-- internal/app/enaptercli/cmd_site_list.go | 26 ++++++++++++++++++++++++ internal/app/enaptercli/errors.go | 2 ++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/internal/app/enaptercli/cmd_site_get.go b/internal/app/enaptercli/cmd_site_get.go index 541a6fe..099c76b 100644 --- a/internal/app/enaptercli/cmd_site_get.go +++ b/internal/app/enaptercli/cmd_site_get.go @@ -1,6 +1,7 @@ package enaptercli import ( + "cmp" "context" "net/http" @@ -32,13 +33,21 @@ func (c *cmdSiteGet) Flags() []cli.Flag { Name: "site-id", Usage: "site ID", Destination: &c.siteID, - Required: true, }) } func (c *cmdSiteGet) do(ctx context.Context) error { + if c.cmdBase.siteID != "" && c.siteID != "" && c.cmdBase.siteID != c.siteID { + return errSiteIDMismatch + } + + siteID := cmp.Or(c.siteID, c.cmdBase.siteID) + if siteID == "" { + return errSiteIDMissing + } + return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, - Path: "/" + c.siteID, + Path: "/" + siteID, }) } diff --git a/internal/app/enaptercli/cmd_site_list.go b/internal/app/enaptercli/cmd_site_list.go index 2290e39..e7d0fe1 100644 --- a/internal/app/enaptercli/cmd_site_list.go +++ b/internal/app/enaptercli/cmd_site_list.go @@ -2,6 +2,8 @@ package enaptercli import ( "context" + "encoding/json" + "fmt" "net/http" "github.com/urfave/cli/v2" @@ -42,6 +44,30 @@ func (c *cmdSiteList) Flags() []cli.Flag { } func (c *cmdSiteList) do(ctx context.Context) error { + if c.siteID != "" { + fmt.Fprintln(c.errWriter, "WARNING: trying to get sites list when site ID "+ + "is set for current connection, result will contain only one site.") + + var site json.RawMessage + if err := c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/" + c.siteID, + RespProcessor: func(r *http.Response) error { + return json.NewDecoder(r.Body).Decode(&site) + }, + }); err != nil { + return err + } + + return json.NewEncoder(c.writer).Encode(struct { + Sites []json.RawMessage `json:"sites"` + TotalCount int `json:"total_count"` + }{ + Sites: []json.RawMessage{site}, + TotalCount: 1, + }) + } + doPaginateRequestParams := paginateHTTPRequestParams{ ObjectName: "sites", Limit: c.limit, diff --git a/internal/app/enaptercli/errors.go b/internal/app/enaptercli/errors.go index 0a4901a..44d17b2 100644 --- a/internal/app/enaptercli/errors.go +++ b/internal/app/enaptercli/errors.go @@ -3,6 +3,8 @@ package enaptercli import "errors" var ( + errSiteIDMismatch = errors.New("passed site-ID must match the site-ID of the current connection") + errSiteIDMissing = errors.New("site ID is required") errUnsupportedFlagValue = errors.New("unsupported flag value") errOnlyOneBlueprinFlag = errors.New("only one of --blueprint-id or --blueprint-path can be specified") errMissedBlueprintFlag = errors.New("one of --blueprint-id or --blueprint-path must be specified") From 847b1373003da5313feca2bb16141d9096f14f91 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Wed, 24 Dec 2025 20:38:22 +0400 Subject: [PATCH 85/88] feat: use associated site-id for device commands --- internal/app/enaptercli/cmd_device.go | 44 ++++++++++++++++++- internal/app/enaptercli/cmd_device_list.go | 11 ----- internal/app/enaptercli/cmd_device_logs.go | 4 +- .../app/enaptercli/cmd_device_telemetry.go | 2 +- .../helps/enapter device change-blueprint | 1 + .../helps/enapter device command execute | 1 + .../testdata/helps/enapter device command get | 1 + .../helps/enapter device command list | 1 + ...apter device communication-config generate | 1 + .../testdata/helps/enapter device delete | 1 + .../testdata/helps/enapter device get | 1 + .../testdata/helps/enapter device list | 2 +- .../testdata/helps/enapter device logs | 1 + .../helps/enapter device run-terminal | 1 + .../testdata/helps/enapter device telemetry | 1 + .../testdata/helps/enapter device update | 1 + 16 files changed, 56 insertions(+), 18 deletions(-) diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index b07282a..039316a 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -1,6 +1,7 @@ package enaptercli import ( + "cmp" "context" "fmt" "net/url" @@ -10,6 +11,7 @@ import ( type cmdDevice struct { cmdBase + siteID string } func buildCmdDevice() *cli.Command { @@ -34,15 +36,33 @@ func buildCmdDevice() *cli.Command { } } +func (c *cmdDevice) Flags() []cli.Flag { + flags := c.cmdBase.Flags() + return append(flags, &cli.StringFlag{ + Name: "site-id", + Usage: "site ID", + Destination: &c.siteID, + }) +} + func (c *cmdDevice) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { - path, err := url.JoinPath("/devices", p.Path) + path, err := c.buildPath(p.Path) if err != nil { - return fmt.Errorf("join path: %w", err) + return err } p.Path = path return c.cmdBase.doHTTPRequest(ctx, p) } +func (c *cmdDevice) runWebSocket(ctx context.Context, p runWebSocketParams) error { + path, err := c.buildPath(p.Path) + if err != nil { + return err + } + p.Path = path + return c.cmdBase.runWebSocket(ctx, p) +} + func (c *cmdDevice) validateExpandFlag(cliCtx *cli.Context) error { return validateExpandFlag(cliCtx, c.supportedExpandFields()) } @@ -50,3 +70,23 @@ func (c *cmdDevice) validateExpandFlag(cliCtx *cli.Context) error { func (c *cmdDevice) supportedExpandFields() []string { return []string{"connectivity", "manifest", "properties", "communication", "site"} } + +func (c *cmdDevice) buildPath(p string) (string, error) { + if c.siteID != "" && c.cmdBase.siteID != "" && c.cmdBase.siteID != c.siteID { + return "", errSiteIDMismatch + } + + path, err := url.JoinPath("/devices", p) + if err != nil { + return "", fmt.Errorf("join path: %w", err) + } + + if siteID := cmp.Or(c.siteID, c.cmdBase.siteID); siteID != "" { + path, err = url.JoinPath("/sites", siteID, path) + if err != nil { + return "", fmt.Errorf("join path: %w", err) + } + } + + return path, nil +} diff --git a/internal/app/enaptercli/cmd_device_list.go b/internal/app/enaptercli/cmd_device_list.go index 528a1f2..808eb48 100644 --- a/internal/app/enaptercli/cmd_device_list.go +++ b/internal/app/enaptercli/cmd_device_list.go @@ -11,7 +11,6 @@ import ( type cmdDeviceList struct { cmdDevice - siteID string expand []string limit int } @@ -39,10 +38,6 @@ func (c *cmdDeviceList) Flags() []cli.Flag { strings.Join(c.supportedExpandFields(), ", ") + ")", }, Destination: &c.expand, - }, &cli.StringFlag{ - Name: "site-id", - Usage: "list devices from this site", - Destination: &c.siteID, }, &cli.IntFlag{ Name: "limit", Usage: "maximum number of devices to retrieve", @@ -75,11 +70,5 @@ func (c *cmdDeviceList) do(ctx context.Context) error { }, } - if c.siteID != "" { - doPaginateRequestParams.BaseParams.Query.Set("site_id", c.siteID) - doPaginateRequestParams.BaseParams.Path = "/sites/" + c.siteID + "/devices" - doPaginateRequestParams.DoFn = c.cmdBase.doHTTPRequest - } - return c.doPaginateRequest(ctx, doPaginateRequestParams) } diff --git a/internal/app/enaptercli/cmd_device_logs.go b/internal/app/enaptercli/cmd_device_logs.go index 020bf19..b1307a9 100644 --- a/internal/app/enaptercli/cmd_device_logs.go +++ b/internal/app/enaptercli/cmd_device_logs.go @@ -133,10 +133,8 @@ func (c *cmdDeviceLogs) doFollow(ctx context.Context) error { query.Add("show", c.showFilter) } - path := fmt.Sprintf("/devices/%s/logs", c.deviceID) - return c.runWebSocket(ctx, runWebSocketParams{ - Path: path, + Path: "/" + c.deviceID + "/logs", Query: query, RespProcessor: func(r io.Reader) error { var msg struct { diff --git a/internal/app/enaptercli/cmd_device_telemetry.go b/internal/app/enaptercli/cmd_device_telemetry.go index a030cfb..d86b52a 100644 --- a/internal/app/enaptercli/cmd_device_telemetry.go +++ b/internal/app/enaptercli/cmd_device_telemetry.go @@ -55,7 +55,7 @@ func (c *cmdDeviceTelemetry) do(ctx context.Context) error { func (c *cmdDeviceTelemetry) doFollow(ctx context.Context) error { return c.runWebSocket(ctx, runWebSocketParams{ - Path: "/devices/" + c.deviceID + "/telemetry", + Path: "/" + c.deviceID + "/telemetry", RespProcessor: func(r io.Reader) error { payload, err := io.ReadAll(r) if err != nil { diff --git a/internal/app/enaptercli/testdata/helps/enapter device change-blueprint b/internal/app/enaptercli/testdata/helps/enapter device change-blueprint index 450f917..c5d106a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device change-blueprint +++ b/internal/app/enaptercli/testdata/helps/enapter device change-blueprint @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --device-id value, -d value device ID --blueprint-id value, -b value blueprint ID to use as new device blueprint --blueprint-path value blueprint path (zip file or directory) to use as new device blueprint diff --git a/internal/app/enaptercli/testdata/helps/enapter device command execute b/internal/app/enaptercli/testdata/helps/enapter device command execute index 665ab2e..b041eda 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command execute +++ b/internal/app/enaptercli/testdata/helps/enapter device command execute @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --device-id value, -d value device ID --name value command name --arguments value command arguments (should be a JSON string) diff --git a/internal/app/enaptercli/testdata/helps/enapter device command get b/internal/app/enaptercli/testdata/helps/enapter device command get index 8b1ea7c..45b0868 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command get +++ b/internal/app/enaptercli/testdata/helps/enapter device command get @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --device-id value, -d value device ID --execution-id value execution ID --expand value [ --expand value ] coma-separated list of expanded options (supported values: log) diff --git a/internal/app/enaptercli/testdata/helps/enapter device command list b/internal/app/enaptercli/testdata/helps/enapter device command list index c0c0b33..a189c0b 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device command list +++ b/internal/app/enaptercli/testdata/helps/enapter device command list @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --device-id value, -d value device ID --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter device communication-config generate b/internal/app/enaptercli/testdata/helps/enapter device communication-config generate index 7867f2e..ffc8338 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device communication-config generate +++ b/internal/app/enaptercli/testdata/helps/enapter device communication-config generate @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --device-id value, -d value device ID --protocol value connection protocol (supported values: MQTT, MQTTS) --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter device delete b/internal/app/enaptercli/testdata/helps/enapter device delete index 8cc1577..44e27cd 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device delete +++ b/internal/app/enaptercli/testdata/helps/enapter device delete @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --device-id value, -d value device ID --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter device get b/internal/app/enaptercli/testdata/helps/enapter device get index afdc0a3..84246aa 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device get +++ b/internal/app/enaptercli/testdata/helps/enapter device get @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --device-id value, -d value device ID --expand value [ --expand value ] coma-separated list of expanded device information (supported values: connectivity, manifest, properties, communication, site) --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter device list b/internal/app/enaptercli/testdata/helps/enapter device list index 5725d68..87711a7 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device list +++ b/internal/app/enaptercli/testdata/helps/enapter device list @@ -8,8 +8,8 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --expand value [ --expand value ] coma-separated list of expanded device information (supported values: connectivity, manifest, properties, communication, site) - --site-id value list devices from this site --limit value maximum number of devices to retrieve (default: retrieves all) --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter device logs b/internal/app/enaptercli/testdata/helps/enapter device logs index e276c97..cef3a68 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device logs +++ b/internal/app/enaptercli/testdata/helps/enapter device logs @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --device-id value, -d value device ID --follow, -f follow the log output (default: false) --from value from timestamp in RFC 3339 format (e.g. 2006-01-02T15:04:05Z) diff --git a/internal/app/enaptercli/testdata/helps/enapter device run-terminal b/internal/app/enaptercli/testdata/helps/enapter device run-terminal index 6eb940e..53ba0f3 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device run-terminal +++ b/internal/app/enaptercli/testdata/helps/enapter device run-terminal @@ -11,6 +11,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --device-id value, -d value gateway device ID --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter device telemetry b/internal/app/enaptercli/testdata/helps/enapter device telemetry index a379f9c..9b759a3 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device telemetry +++ b/internal/app/enaptercli/testdata/helps/enapter device telemetry @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --device-id value, -d value device ID --follow, -f follow the telemetry output (default: false) --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter device update b/internal/app/enaptercli/testdata/helps/enapter device update index 3a8dfce..1e42322 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device update +++ b/internal/app/enaptercli/testdata/helps/enapter device update @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --device-id value, -d value device ID --name value device name --slug value device slug From 8e4088506d0b941caa366f68b9bc5dcd0cabe585 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Wed, 24 Dec 2025 19:32:16 +0400 Subject: [PATCH 86/88] feat: use associated site-id from rule-engine commands --- internal/app/enaptercli/cmd_rule_engine.go | 23 ++++++++++++++++++- .../testdata/helps/enapter rule-engine get | 1 + .../testdata/helps/enapter rule-engine resume | 1 + .../helps/enapter rule-engine rule create | 1 + .../helps/enapter rule-engine rule delete | 1 + .../helps/enapter rule-engine rule disable | 1 + .../helps/enapter rule-engine rule enable | 1 + .../helps/enapter rule-engine rule get | 1 + .../helps/enapter rule-engine rule list | 1 + .../helps/enapter rule-engine rule logs | 1 + .../helps/enapter rule-engine rule update | 1 + .../enapter rule-engine rule update-script | 1 + .../helps/enapter rule-engine suspend | 1 + 13 files changed, 34 insertions(+), 1 deletion(-) diff --git a/internal/app/enaptercli/cmd_rule_engine.go b/internal/app/enaptercli/cmd_rule_engine.go index 8632686..9e818fc 100644 --- a/internal/app/enaptercli/cmd_rule_engine.go +++ b/internal/app/enaptercli/cmd_rule_engine.go @@ -1,6 +1,7 @@ package enaptercli import ( + "cmp" "context" "fmt" "net/url" @@ -10,6 +11,7 @@ import ( type cmdRuleEngine struct { cmdBase + siteID string } func buildCmdRuleEngine() *cli.Command { @@ -27,11 +29,30 @@ func buildCmdRuleEngine() *cli.Command { } } +func (c *cmdRuleEngine) Flags() []cli.Flag { + flags := c.cmdBase.Flags() + return append(flags, &cli.StringFlag{ + Name: "site-id", + Usage: "site ID", + Destination: &c.siteID, + }) +} + func (c *cmdRuleEngine) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { - path, err := url.JoinPath("/site/rule_engine", p.Path) + if c.siteID != "" && c.cmdBase.siteID != "" && c.cmdBase.siteID != c.siteID { + return errSiteIDMismatch + } + + siteID := cmp.Or(c.siteID, c.cmdBase.siteID) + if siteID == "" { + return errSiteIDMissing + } + + path, err := url.JoinPath("/sites/", siteID, "/rule_engine", p.Path) if err != nil { return fmt.Errorf("join path: %w", err) } + p.Path = path return c.cmdBase.doHTTPRequest(ctx, p) } diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine get b/internal/app/enaptercli/testdata/helps/enapter rule-engine get index b44c8e2..e87cfe9 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine get +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine get @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume index 5953592..a25a105 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine resume +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine resume @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create index a0a7ace..24645d9 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule create @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --slug value Slug for the new rule --script value Path to the file containing the script code --runtime-version value Version of the runtime to use for the script execution (default: "V3") diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete index 9d98846..66a260b 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule delete @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --rule-id value Rule ID or slug --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable index 217c3e6..a3e6d94 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule disable @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable index 1419272..b58bad4 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule enable @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --rule-id value [ --rule-id value ] Rule IDs or slugs --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get index 41dd54d..e794ae6 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule get @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --rule-id value Rule ID or slug --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list index 3cd3f96..b400c2a 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule list @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --help, -h show help ENVIRONMENT VARIABLES: diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs index 71efdf8..2d46abc 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule logs @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --rule-id value rule ID --follow, -f follow the log output (default: false) --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update index c9e11f9..9b3f278 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --rule-id value Rule ID or slug to update --slug value A new rule slug --help, -h show help diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script index 0f40b58..e64f5a8 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine rule update-script @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --rule-id value Rule ID or slug to update --script value Path to a file containing the script code --runtime-version value Version of the runtime to use for the script execution (default: "V3") diff --git a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend index 3a50202..e632a47 100644 --- a/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend +++ b/internal/app/enaptercli/testdata/helps/enapter rule-engine suspend @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --help, -h show help ENVIRONMENT VARIABLES: From 669d4a8cc3d71604de25adcb2c73a6f1f7677375 Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Sun, 28 Dec 2025 18:06:08 +0400 Subject: [PATCH 87/88] feat: use associated site-id for provisioning commands --- .../cmd_device_create_lua_device.go | 46 ++++++++++++++++++- .../cmd_device_create_standalone.go | 12 ++++- .../helps/enapter device create lua-device | 1 + 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/internal/app/enaptercli/cmd_device_create_lua_device.go b/internal/app/enaptercli/cmd_device_create_lua_device.go index 4968e2c..54060a4 100644 --- a/internal/app/enaptercli/cmd_device_create_lua_device.go +++ b/internal/app/enaptercli/cmd_device_create_lua_device.go @@ -2,6 +2,7 @@ package enaptercli import ( "bytes" + "cmp" "context" "encoding/json" "fmt" @@ -12,6 +13,7 @@ import ( type cmdDeviceCreateLua struct { cmdDeviceCreate + siteID string deviceName string deviceSlug string runtimeID string @@ -36,6 +38,10 @@ func buildCmdDeviceCreateLua() *cli.Command { func (c *cmdDeviceCreateLua) Flags() []cli.Flag { flags := c.cmdDeviceCreate.Flags() return append(flags, &cli.StringFlag{ + Name: "site-id", + Usage: "site ID", + Destination: &c.siteID, + }, &cli.StringFlag{ Name: "runtime-id", Aliases: []string{"r"}, Usage: "UCM device ID where the new Lua device will run", @@ -85,8 +91,14 @@ func (c *cmdDeviceCreateLua) do(ctx context.Context) error { c.blueprintID = blueprintID } + // Cloud API does not allow slugs as runtime ID for now + runtimeID, err := c.resolveRuntimeID(ctx) + if err != nil { + return fmt.Errorf("resolve runtime ID: %w", err) + } + body, err := json.Marshal(map[string]interface{}{ - "runtime_id": c.runtimeID, + "runtime_id": runtimeID, "name": c.deviceName, "slug": c.deviceSlug, "blueprint_id": c.blueprintID, @@ -101,3 +113,35 @@ func (c *cmdDeviceCreateLua) do(ctx context.Context) error { ContentType: contentTypeJSON, }) } + +func (c *cmdDeviceCreateLua) resolveRuntimeID(ctx context.Context) (string, error) { + if c.siteID != "" && c.cmdBase.siteID != "" && c.cmdBase.siteID != c.siteID { + return "", errSiteIDMismatch + } + + siteID := cmp.Or(c.siteID, c.cmdBase.siteID) + if siteID == "" { + return c.runtimeID, nil + } + + var resp struct { + Device struct { + ID string `json:"id"` + } `json:"device"` + } + + if err := c.doHTTPRequest(ctx, doHTTPRequestParams{ + Method: http.MethodGet, + Path: "/sites/" + siteID + "/devices/" + c.runtimeID, + RespProcessor: func(r *http.Response) error { + if r.StatusCode != http.StatusOK { + return cli.Exit(parseRespErrorMessage(r), 1) + } + return json.NewDecoder(r.Body).Decode(&resp) + }, + }); err != nil { + return "", err + } + + return resp.Device.ID, nil +} diff --git a/internal/app/enaptercli/cmd_device_create_standalone.go b/internal/app/enaptercli/cmd_device_create_standalone.go index b9ce065..b83a3a3 100644 --- a/internal/app/enaptercli/cmd_device_create_standalone.go +++ b/internal/app/enaptercli/cmd_device_create_standalone.go @@ -2,6 +2,7 @@ package enaptercli import ( "bytes" + "cmp" "context" "encoding/json" "fmt" @@ -52,8 +53,17 @@ func (c *cmdDeviceCreateStandalone) Flags() []cli.Flag { } func (c *cmdDeviceCreateStandalone) do(ctx context.Context) error { + if c.siteID != "" && c.cmdBase.siteID != "" && c.cmdBase.siteID != c.siteID { + return errSiteIDMismatch + } + + siteID := cmp.Or(c.siteID, c.cmdBase.siteID) + if siteID == "" { + return errSiteIDMissing + } + body, err := json.Marshal(map[string]any{ - "site_id": c.siteID, + "site_id": siteID, "name": c.deviceName, "slug": c.deviceSlug, }) diff --git a/internal/app/enaptercli/testdata/helps/enapter device create lua-device b/internal/app/enaptercli/testdata/helps/enapter device create lua-device index 8e634b2..fe4ea93 100644 --- a/internal/app/enaptercli/testdata/helps/enapter device create lua-device +++ b/internal/app/enaptercli/testdata/helps/enapter device create lua-device @@ -8,6 +8,7 @@ OPTIONS: --connection value, -c value name of the connection to use --api-allow-insecure allow insecure connections to the Enapter API (default: false) [$ENAPTER3_API_ALLOW_INSECURE] --verbose log extra details about the operation (default: false) + --site-id value site ID --runtime-id value, -r value UCM device ID where the new Lua device will run --device-name value, -n value name for the new Lua device --device-slug value slug for the new Lua device From e43f62f0603ff91ca4184340ca67a9dbce240cbe Mon Sep 17 00:00:00 2001 From: Daniil Poroshin Date: Tue, 30 Dec 2025 16:55:33 +0400 Subject: [PATCH 88/88] test: add more tests --- internal/app/configfile/config.go | 24 +++- internal/app/enaptercli/app_test.go | 7 +- internal/app/enaptercli/cmd_base.go | 12 ++ internal/app/enaptercli/cmd_device.go | 20 +-- .../cmd_device_create_lua_device.go | 15 +- .../cmd_device_create_standalone.go | 11 +- internal/app/enaptercli/cmd_rule_engine.go | 11 +- internal/app/enaptercli/cmd_site_get.go | 12 +- internal/app/enaptercli/cmd_site_list.go | 10 +- internal/app/enaptercli/execute_test.go | 132 +++++++++++------- .../blueprint_get_by_id/cmd.tmpl | 2 + .../out | 0 .../req_0 | 0 .../resp_0 | 0 .../blueprint_get_by_name/cmd.tmpl | 2 + .../out | 0 .../req_0 | 0 .../resp_0 | 0 .../blueprint_inspect_by_id/cmd.tmpl | 1 - .../blueprint_inspect_by_name/cmd.tmpl | 1 - .../credentials_by_connection_name/cmd.tmpl | 2 + .../credentials_by_connection_name/out | 1 + .../credentials_by_connection_name/req_0 | 19 +++ .../credentials_by_connection_name/resp_0 | 1 + .../cmd.tmpl | 3 + .../credentials_by_default_connection/out | 1 + .../credentials_by_default_connection/req_0 | 19 +++ .../credentials_by_default_connection/resp_0 | 1 + .../credentials_by_flags/cmd.tmpl | 1 + .../http_req_resp/credentials_by_flags/out | 1 + .../http_req_resp/credentials_by_flags/req_0 | 19 +++ .../http_req_resp/credentials_by_flags/resp_0 | 1 + .../credentials_missing/cmd.tmpl | 1 + .../http_req_resp/credentials_missing/out | 10 ++ .../http_req_resp/credentials_mixed/cmd.tmpl | 2 + .../http_req_resp/credentials_mixed/out | 2 + .../http_req_resp/credentials_mixed/req_0 | 19 +++ .../http_req_resp/credentials_mixed/resp_0 | 1 + .../credentials_site_id_mismatch/cmd.tmpl | 2 + .../credentials_site_id_mismatch/out | 1 + .../credentials_site_id_redundancy/cmd.tmpl | 2 + .../credentials_site_id_redundancy/out | 1 + .../credentials_site_id_redundancy/req_0 | 19 +++ .../credentials_site_id_redundancy/resp_0 | 1 + .../device_assign_blueprint_by_id/cmd.tmpl | 1 - .../cmd.tmpl | 1 - .../device_assign_blueprint_by_path/cmd.tmpl | 1 - .../device_assign_blueprint_by_zip/cmd.tmpl | 1 - .../cmd.tmpl | 1 - .../device_change_blueprint_by_id/cmd.tmpl | 2 + .../out | 0 .../req_0 | 0 .../resp_0 | 0 .../cmd.tmpl | 2 + .../out | 0 .../device_change_blueprint_by_path/cmd.tmpl | 2 + .../out | 0 .../req_0 | 0 .../req_1 | 0 .../resp_0 | 0 .../resp_1 | 0 .../device_change_blueprint_by_zip/cmd.tmpl | 2 + .../out | 0 .../req_0 | 0 .../req_1 | 0 .../resp_0 | 0 .../resp_1 | 0 .../cmd.tmpl | 2 + .../out | 0 .../cmd.tmpl | 2 + .../out | 1 + .../device_create_lua_with_site_id/cmd.tmpl | 2 + .../device_create_lua_with_site_id/out | 1 + .../device_create_lua_with_site_id/req_0 | 19 +++ .../device_create_lua_with_site_id/req_1 | 22 +++ .../device_create_lua_with_site_id/resp_0 | 1 + .../device_create_lua_with_site_id/resp_1 | 1 + .../cmd.tmpl | 2 + .../device_create_lua_without_blueprint/out | 1 + .../cmd.tmpl | 2 + .../device_create_lua_without_site_id/out | 1 + .../device_create_lua_without_site_id/req_0 | 22 +++ .../device_create_lua_without_site_id/resp_0 | 1 + .../cmd.tmpl | 2 + .../out | 1 + .../device_get_with_site_id/cmd.tmpl | 2 + .../http_req_resp/device_get_with_site_id/out | 1 + .../device_get_with_site_id/req_0 | 19 +++ .../device_get_with_site_id/resp_0 | 1 + .../device_get_without_site_id/cmd.tmpl | 2 + .../device_get_without_site_id/out | 1 + .../device_get_without_site_id/req_0 | 19 +++ .../device_get_without_site_id/resp_0 | 1 + .../rule_engine_get_with_site_id/cmd.tmpl | 2 + .../rule_engine_get_with_site_id/out | 1 + .../rule_engine_get_with_site_id/req_0 | 19 +++ .../rule_engine_get_with_site_id/resp_0 | 1 + .../rule_engine_get_without_site_id/cmd.tmpl | 2 + .../rule_engine_get_without_site_id/out | 1 + .../site_get_with_site_id/cmd.tmpl | 2 + .../http_req_resp/site_get_with_site_id/out | 1 + .../http_req_resp/site_get_with_site_id/req_0 | 19 +++ .../site_get_with_site_id/resp_0 | 1 + .../site_get_without_site_id/cmd.tmpl | 2 + .../site_get_without_site_id/out | 1 + .../site_list_with_site_id/cmd.tmpl | 2 + .../http_req_resp/site_list_with_site_id/out | 2 + .../site_list_with_site_id/req_0 | 19 +++ .../site_list_with_site_id/resp_0 | 1 + .../site_list_without_site_id/cmd.tmpl | 2 + .../site_list_without_site_id/out | 1 + .../site_list_without_site_id/req_0 | 19 +++ .../site_list_without_site_id/req_1 | 19 +++ .../site_list_without_site_id/resp_0 | 1 + .../site_list_without_site_id/resp_1 | 1 + 115 files changed, 538 insertions(+), 113 deletions(-) create mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_id/cmd.tmpl rename internal/app/enaptercli/testdata/http_req_resp/{blueprint_inspect_by_id => blueprint_get_by_id}/out (100%) rename internal/app/enaptercli/testdata/http_req_resp/{blueprint_inspect_by_id => blueprint_get_by_id}/req_0 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{blueprint_inspect_by_id => blueprint_get_by_id}/resp_0 (100%) create mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_name/cmd.tmpl rename internal/app/enaptercli/testdata/http_req_resp/{blueprint_inspect_by_name => blueprint_get_by_name}/out (100%) rename internal/app/enaptercli/testdata/http_req_resp/{blueprint_inspect_by_name => blueprint_get_by_name}/req_0 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{blueprint_inspect_by_name => blueprint_get_by_name}/resp_0 (100%) delete mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl delete mode 100644 internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_missing/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_missing/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_mismatch/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_mismatch/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/resp_0 delete mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/cmd.tmpl delete mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/cmd.tmpl delete mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/cmd.tmpl delete mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/cmd.tmpl delete mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id/cmd.tmpl rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_id => device_change_blueprint_by_id}/out (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_id => device_change_blueprint_by_id}/req_0 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_id => device_change_blueprint_by_id}/resp_0 (100%) create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id_and_path/cmd.tmpl rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_id_and_path => device_change_blueprint_by_id_and_path}/out (100%) create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/cmd.tmpl rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_path => device_change_blueprint_by_path}/out (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_path => device_change_blueprint_by_path}/req_0 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_path => device_change_blueprint_by_path}/req_1 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_path => device_change_blueprint_by_path}/resp_0 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_path => device_change_blueprint_by_path}/resp_1 (100%) create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/cmd.tmpl rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_zip => device_change_blueprint_by_zip}/out (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_zip => device_change_blueprint_by_zip}/req_0 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_zip => device_change_blueprint_by_zip}/req_1 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_zip => device_change_blueprint_by_zip}/resp_0 (100%) rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_by_zip => device_change_blueprint_by_zip}/resp_1 (100%) create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_without_blueprint/cmd.tmpl rename internal/app/enaptercli/testdata/http_req_resp/{device_assign_blueprint_without_blueprint => device_change_blueprint_without_blueprint}/out (100%) create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_blueprint_id_and_path/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_blueprint_id_and_path/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/req_1 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/resp_1 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_blueprint/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_blueprint/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_standalone_without_site_id/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_create_standalone_without_site_id/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_without_site_id/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_without_site_id/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_get_without_site_id/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_get_without_site_id/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/cmd.tmpl create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/out create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/req_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/req_1 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/resp_0 create mode 100644 internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/resp_1 diff --git a/internal/app/configfile/config.go b/internal/app/configfile/config.go index fa1e7f1..e479e8f 100644 --- a/internal/app/configfile/config.go +++ b/internal/app/configfile/config.go @@ -32,12 +32,12 @@ const ( ) func Load() (Config, error) { - home, err := os.UserHomeDir() + dir, err := configDir() if err != nil { - return Config{}, fmt.Errorf("get home dir: %w", err) + return Config{}, err } - path := filepath.Join(home, dirName, fileName) + path := filepath.Join(dir, fileName) f, err := os.Open(path) if err != nil { if errors.Is(err, fs.ErrNotExist) { @@ -56,18 +56,17 @@ func Load() (Config, error) { } func Save(c Config) error { - home, err := os.UserHomeDir() + dir, err := configDir() if err != nil { - return fmt.Errorf("get home dir: %w", err) + return err } const perm = 0o755 - dir := filepath.Join(home, dirName) if err := os.MkdirAll(dir, perm); err != nil { return fmt.Errorf("create config dir: %w", err) } - path := filepath.Join(home, dirName, fileName) + path := filepath.Join(dir, fileName) f, err := os.Create(path) if err != nil { return fmt.Errorf("create config file: %w", err) @@ -82,3 +81,14 @@ func Save(c Config) error { return f.Sync() } + +func configDir() (string, error) { + if p := os.Getenv("ENAPTER3_CONFIG"); p != "" { + return p, nil + } + home, err := os.UserHomeDir() + if err != nil { + return "", fmt.Errorf("get home dir: %w", err) + } + return filepath.Join(home, dirName), nil +} diff --git a/internal/app/enaptercli/app_test.go b/internal/app/enaptercli/app_test.go index 2b066a6..271ad05 100644 --- a/internal/app/enaptercli/app_test.go +++ b/internal/app/enaptercli/app_test.go @@ -19,19 +19,17 @@ var errExitTimeout = errors.New("exit timed out") type testApp struct { app *cli.App outBuf *lineBuffer - errBuf *bytes.Buffer errCh chan error cancel func() } func startTestApp(args ...string) *testApp { outBuf := newLineBuffer() - errBuf := &bytes.Buffer{} app := enaptercli.NewApp() app.HideVersion = true app.Writer = outBuf - app.ErrWriter = errBuf + app.ErrWriter = outBuf app.ExitErrHandler = func(*cli.Context, error) {} errCh := make(chan error, 1) @@ -43,7 +41,6 @@ func startTestApp(args ...string) *testApp { return &testApp{ app: app, outBuf: outBuf, - errBuf: errBuf, errCh: errCh, cancel: cancel, } @@ -63,7 +60,7 @@ func (a *testApp) Wait() error { } } -func (a *testApp) Stdout() *lineBuffer { +func (a *testApp) Output() *lineBuffer { return a.outBuf } diff --git a/internal/app/enaptercli/cmd_base.go b/internal/app/enaptercli/cmd_base.go index e588501..a111cee 100644 --- a/internal/app/enaptercli/cmd_base.go +++ b/internal/app/enaptercli/cmd_base.go @@ -2,6 +2,7 @@ package enaptercli import ( "bytes" + "cmp" "context" "crypto/tls" "encoding/base64" @@ -155,6 +156,17 @@ func (c *cmdBase) SubcommandHelpTemplate() string { return cli.SubcommandHelpTemplate + enapterAPIEnvVarsHelp } +func (c *cmdBase) chooseSiteID(cmdSiteID string) (string, error) { + if cmdSiteID != "" && c.siteID != "" && c.siteID != cmdSiteID { + return "", errSiteIDMismatch + } + siteID := cmp.Or(cmdSiteID, c.siteID) + if siteID == "" { + return "", errSiteIDMissing + } + return siteID, nil +} + type doHTTPRequestParams struct { Method string Path string diff --git a/internal/app/enaptercli/cmd_device.go b/internal/app/enaptercli/cmd_device.go index 039316a..85a4ae2 100644 --- a/internal/app/enaptercli/cmd_device.go +++ b/internal/app/enaptercli/cmd_device.go @@ -1,8 +1,8 @@ package enaptercli import ( - "cmp" "context" + "errors" "fmt" "net/url" @@ -72,20 +72,22 @@ func (c *cmdDevice) supportedExpandFields() []string { } func (c *cmdDevice) buildPath(p string) (string, error) { - if c.siteID != "" && c.cmdBase.siteID != "" && c.cmdBase.siteID != c.siteID { - return "", errSiteIDMismatch - } - path, err := url.JoinPath("/devices", p) if err != nil { return "", fmt.Errorf("join path: %w", err) } - if siteID := cmp.Or(c.siteID, c.cmdBase.siteID); siteID != "" { - path, err = url.JoinPath("/sites", siteID, path) - if err != nil { - return "", fmt.Errorf("join path: %w", err) + siteID, err := c.chooseSiteID(c.siteID) + if err != nil { + if errors.Is(err, errSiteIDMissing) { + return path, nil } + return "", err + } + + path, err = url.JoinPath("/sites", siteID, path) + if err != nil { + return "", fmt.Errorf("join path: %w", err) } return path, nil diff --git a/internal/app/enaptercli/cmd_device_create_lua_device.go b/internal/app/enaptercli/cmd_device_create_lua_device.go index 54060a4..8edbf66 100644 --- a/internal/app/enaptercli/cmd_device_create_lua_device.go +++ b/internal/app/enaptercli/cmd_device_create_lua_device.go @@ -2,9 +2,9 @@ package enaptercli import ( "bytes" - "cmp" "context" "encoding/json" + "errors" "fmt" "net/http" @@ -115,13 +115,12 @@ func (c *cmdDeviceCreateLua) do(ctx context.Context) error { } func (c *cmdDeviceCreateLua) resolveRuntimeID(ctx context.Context) (string, error) { - if c.siteID != "" && c.cmdBase.siteID != "" && c.cmdBase.siteID != c.siteID { - return "", errSiteIDMismatch - } - - siteID := cmp.Or(c.siteID, c.cmdBase.siteID) - if siteID == "" { - return c.runtimeID, nil + siteID, err := c.chooseSiteID(c.siteID) + if err != nil { + if errors.Is(err, errSiteIDMissing) { + return c.runtimeID, nil + } + return "", err } var resp struct { diff --git a/internal/app/enaptercli/cmd_device_create_standalone.go b/internal/app/enaptercli/cmd_device_create_standalone.go index b83a3a3..39d7090 100644 --- a/internal/app/enaptercli/cmd_device_create_standalone.go +++ b/internal/app/enaptercli/cmd_device_create_standalone.go @@ -2,7 +2,6 @@ package enaptercli import ( "bytes" - "cmp" "context" "encoding/json" "fmt" @@ -53,13 +52,9 @@ func (c *cmdDeviceCreateStandalone) Flags() []cli.Flag { } func (c *cmdDeviceCreateStandalone) do(ctx context.Context) error { - if c.siteID != "" && c.cmdBase.siteID != "" && c.cmdBase.siteID != c.siteID { - return errSiteIDMismatch - } - - siteID := cmp.Or(c.siteID, c.cmdBase.siteID) - if siteID == "" { - return errSiteIDMissing + siteID, err := c.chooseSiteID(c.siteID) + if err != nil { + return err } body, err := json.Marshal(map[string]any{ diff --git a/internal/app/enaptercli/cmd_rule_engine.go b/internal/app/enaptercli/cmd_rule_engine.go index 9e818fc..16c0a56 100644 --- a/internal/app/enaptercli/cmd_rule_engine.go +++ b/internal/app/enaptercli/cmd_rule_engine.go @@ -1,7 +1,6 @@ package enaptercli import ( - "cmp" "context" "fmt" "net/url" @@ -39,13 +38,9 @@ func (c *cmdRuleEngine) Flags() []cli.Flag { } func (c *cmdRuleEngine) doHTTPRequest(ctx context.Context, p doHTTPRequestParams) error { - if c.siteID != "" && c.cmdBase.siteID != "" && c.cmdBase.siteID != c.siteID { - return errSiteIDMismatch - } - - siteID := cmp.Or(c.siteID, c.cmdBase.siteID) - if siteID == "" { - return errSiteIDMissing + siteID, err := c.chooseSiteID(c.siteID) + if err != nil { + return err } path, err := url.JoinPath("/sites/", siteID, "/rule_engine", p.Path) diff --git a/internal/app/enaptercli/cmd_site_get.go b/internal/app/enaptercli/cmd_site_get.go index 099c76b..929904c 100644 --- a/internal/app/enaptercli/cmd_site_get.go +++ b/internal/app/enaptercli/cmd_site_get.go @@ -1,7 +1,6 @@ package enaptercli import ( - "cmp" "context" "net/http" @@ -37,15 +36,10 @@ func (c *cmdSiteGet) Flags() []cli.Flag { } func (c *cmdSiteGet) do(ctx context.Context) error { - if c.cmdBase.siteID != "" && c.siteID != "" && c.cmdBase.siteID != c.siteID { - return errSiteIDMismatch + siteID, err := c.chooseSiteID(c.siteID) + if err != nil { + return err } - - siteID := cmp.Or(c.siteID, c.cmdBase.siteID) - if siteID == "" { - return errSiteIDMissing - } - return c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, Path: "/" + siteID, diff --git a/internal/app/enaptercli/cmd_site_list.go b/internal/app/enaptercli/cmd_site_list.go index e7d0fe1..c43854f 100644 --- a/internal/app/enaptercli/cmd_site_list.go +++ b/internal/app/enaptercli/cmd_site_list.go @@ -44,16 +44,18 @@ func (c *cmdSiteList) Flags() []cli.Flag { } func (c *cmdSiteList) do(ctx context.Context) error { - if c.siteID != "" { + if siteID, _ := c.chooseSiteID(""); siteID != "" { fmt.Fprintln(c.errWriter, "WARNING: trying to get sites list when site ID "+ "is set for current connection, result will contain only one site.") - var site json.RawMessage + var resp struct { + Site json.RawMessage `json:"site"` + } if err := c.doHTTPRequest(ctx, doHTTPRequestParams{ Method: http.MethodGet, Path: "/" + c.siteID, RespProcessor: func(r *http.Response) error { - return json.NewDecoder(r.Body).Decode(&site) + return json.NewDecoder(r.Body).Decode(&resp) }, }); err != nil { return err @@ -63,7 +65,7 @@ func (c *cmdSiteList) do(ctx context.Context) error { Sites []json.RawMessage `json:"sites"` TotalCount int `json:"total_count"` }{ - Sites: []json.RawMessage{site}, + Sites: []json.RawMessage{resp.Site}, TotalCount: 1, }) } diff --git a/internal/app/enaptercli/execute_test.go b/internal/app/enaptercli/execute_test.go index 733c866..9227850 100644 --- a/internal/app/enaptercli/execute_test.go +++ b/internal/app/enaptercli/execute_test.go @@ -29,7 +29,7 @@ func TestHelpMessages(t *testing.T) { app := startTestApp(args...) appErr := app.Wait() - actual, err := io.ReadAll(app.Stdout()) + actual, err := io.ReadAll(app.Output()) require.NoError(t, err) if appErr != nil { @@ -54,59 +54,95 @@ func TestHTTPReqResp(t *testing.T) { for _, tc := range tests { t.Run(tc.Name(), func(t *testing.T) { - reqCount := 0 - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - reqObj := struct { - Method string - URL string - Header http.Header - Body string - }{ - r.Method, - r.URL.String(), - r.Header, - readBodyAsString(t, r.Body), - } - expReqFileName := filepath.Join(testdataPath, tc.Name(), "req_"+strconv.Itoa(reqCount)) - if update { - err := os.WriteFile(expReqFileName, shouldMarshalIndent(t, reqObj), 0o600) - require.NoError(t, err) - } else { - require.Equal(t, readFileToString(t, expReqFileName), string(shouldMarshalIndent(t, reqObj))) - } - - resp := shouldReadFile(t, filepath.Join(testdataPath, tc.Name(), "resp_"+strconv.Itoa(reqCount))) - _, _ = w.Write(resp) - - reqCount++ - })) - defer srv.Close() - - tmplParams := struct{ BaseFlags string }{ - BaseFlags: strings.Join([]string{"--token", testToken, "--api-url", srv.URL}, " "), - } + path := filepath.Join(testdataPath, tc.Name()) + testExecute(t, path) + }) + } +} - cmd := executeTmpl(t, filepath.Join(testdataPath, tc.Name(), "cmd.tmpl"), tmplParams) - args := strings.Split(cmd, " ") - app := startTestApp(args...) - appErr := app.Wait() +func testExecute(t *testing.T, path string) { + srv := newTestServer(t, path) + + cmd := executeTmpl(t, filepath.Join(path, "cmd.tmpl"), struct { + Token string + URL string + }{ + Token: testToken, + URL: srv.URL, + }) + + t.Setenv("ENAPTER3_CONFIG", t.TempDir()) + output := executeCommands(t, cmd) + + exepctedOutFileName := filepath.Join(path, "out") + if update { + err := os.WriteFile(exepctedOutFileName, output, 0o600) + require.NoError(t, err) + } else { + expected := readFileToString(t, exepctedOutFileName) + require.Equal(t, expected, string(output)) + } +} + +func newTestServer(t *testing.T, path string) *httptest.Server { + t.Helper() - actual, err := io.ReadAll(app.Stdout()) + reqCount := 0 + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + reqPath := filepath.Join(path, "req_"+strconv.Itoa(reqCount)) + respPath := filepath.Join(path, "resp_"+strconv.Itoa(reqCount)) + + info := struct { + Method string + URL string + Header http.Header + Body string + }{ + Method: r.Method, + URL: r.URL.String(), + Header: r.Header, + Body: readBodyAsString(t, r.Body), + } + if update { + err := os.WriteFile(reqPath, shouldMarshalIndent(t, info), 0o600) require.NoError(t, err) + } else { + expected := readFileToString(t, reqPath) + actual := string(shouldMarshalIndent(t, info)) + require.Equal(t, expected, actual) + } - if appErr != nil { - actual = append(actual, []byte("app exit with error: "+appErr.Error()+"\n")...) - } + resp := shouldReadFile(t, respPath) + _, _ = w.Write(resp) - exepctedOutFileName := filepath.Join(testdataPath, tc.Name(), "out") - if update { - err := os.WriteFile(exepctedOutFileName, actual, 0o600) - require.NoError(t, err) - } else { - require.Equal(t, readFileToString(t, exepctedOutFileName), string(actual)) - } - }) + reqCount++ + })) + t.Cleanup(func() { srv.Close() }) + + return srv +} + +func executeCommands(t *testing.T, cmd string) []byte { + t.Helper() + + var output []byte + for cmd := range strings.Lines(cmd) { + cmd := strings.Trim(cmd, "\n") + args := strings.Split(cmd, " ") + + app := startTestApp(args...) + appErr := app.Wait() + + out, err := io.ReadAll(app.Output()) + require.NoError(t, err) + + output = append(output, out...) + if appErr != nil { + output = append(output, []byte("app exit with error: "+appErr.Error()+"\n")...) + break + } } + return output } func executeTmpl(t *testing.T, tmplFilePath string, tmplParams interface{}) string { diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_id/cmd.tmpl new file mode 100644 index 0000000..0d61ed5 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 blueprint get --connection my-conn --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/out b/internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_id/out similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/out rename to internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_id/out diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_id/req_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/req_0 rename to internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_id/req_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_id/resp_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/resp_0 rename to internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_id/resp_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_name/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_name/cmd.tmpl new file mode 100644 index 0000000..f980f03 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_name/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 blueprint get --connection my-conn --blueprint-id test_blueprint_name diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/out b/internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_name/out similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/out rename to internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_name/out diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/req_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_name/req_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/req_0 rename to internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_name/req_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_name/resp_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/resp_0 rename to internal/app/enaptercli/testdata/http_req_resp/blueprint_get_by_name/resp_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl deleted file mode 100644 index 575c1d6..0000000 --- a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_id/cmd.tmpl +++ /dev/null @@ -1 +0,0 @@ -enapter3 blueprint get {{.BaseFlags}} --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl deleted file mode 100644 index 9a7c247..0000000 --- a/internal/app/enaptercli/testdata/http_req_resp/blueprint_inspect_by_name/cmd.tmpl +++ /dev/null @@ -1 +0,0 @@ -enapter3 blueprint get {{.BaseFlags}} --blueprint-id test_blueprint_name diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/cmd.tmpl new file mode 100644 index 0000000..713bef9 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device get --connection my-conn --device-id my-device diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/out b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/out new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/out @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/req_0 b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/req_0 new file mode 100644 index 0000000..e16fe8e --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/devices/my-device", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/resp_0 new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_connection_name/resp_0 @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/cmd.tmpl new file mode 100644 index 0000000..f0ac177 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/cmd.tmpl @@ -0,0 +1,3 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 connection set-default --name my-conn +enapter3 device get --device-id my-device diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/out b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/out new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/out @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/req_0 b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/req_0 new file mode 100644 index 0000000..e16fe8e --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/devices/my-device", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/resp_0 new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_default_connection/resp_0 @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/cmd.tmpl new file mode 100644 index 0000000..cf34301 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/cmd.tmpl @@ -0,0 +1 @@ +enapter3 device get --token 123 --api-url {{.URL}} --device-id my-device diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/out b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/out new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/out @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/req_0 b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/req_0 new file mode 100644 index 0000000..8945df1 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/devices/my-device", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "123" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/resp_0 new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_by_flags/resp_0 @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_missing/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/credentials_missing/cmd.tmpl new file mode 100644 index 0000000..eb1adbb --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_missing/cmd.tmpl @@ -0,0 +1 @@ +enapter3 device get --device-id my-device diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_missing/out b/internal/app/enaptercli/testdata/http_req_resp/credentials_missing/out new file mode 100644 index 0000000..2acda60 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_missing/out @@ -0,0 +1,10 @@ +app exit with error: No connection configured. + +Please, specify connection using --connection flag. + +To list available connections: +$ enapter3 connection list + +To add a new connection: +$ enapter3 connection add + diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/cmd.tmpl new file mode 100644 index 0000000..3b85c13 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device get --connection my-conn --token {{.Token}} --device-id my-device diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/out b/internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/out new file mode 100644 index 0000000..9d7e60d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/out @@ -0,0 +1,2 @@ +WARNING: credentials set via environment variables or flags are ignored. +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/req_0 b/internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/req_0 new file mode 100644 index 0000000..e16fe8e --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/devices/my-device", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/resp_0 new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_mixed/resp_0 @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_mismatch/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_mismatch/cmd.tmpl new file mode 100644 index 0000000..7250861 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_mismatch/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} --site-id my-site +enapter3 device get --connection my-conn --site-id other-site --device-id my-device diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_mismatch/out b/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_mismatch/out new file mode 100644 index 0000000..ea896f0 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_mismatch/out @@ -0,0 +1 @@ +app exit with error: passed site-ID must match the site-ID of the current connection diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/cmd.tmpl new file mode 100644 index 0000000..9e9d408 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} --site-id my-site +enapter3 device get --connection my-conn --site-id my-site --device-id my-device diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/out b/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/out new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/out @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/req_0 b/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/req_0 new file mode 100644 index 0000000..99cb7dd --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/sites/my-site/devices/my-device", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/resp_0 new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/credentials_site_id_redundancy/resp_0 @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/cmd.tmpl deleted file mode 100644 index c36d0ca..0000000 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/cmd.tmpl +++ /dev/null @@ -1 +0,0 @@ -enapter3 device change-blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/cmd.tmpl deleted file mode 100644 index 503d2e2..0000000 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/cmd.tmpl +++ /dev/null @@ -1 +0,0 @@ -enapter3 device change-blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 --blueprint-path ./testdata/blueprints/bp.zip diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/cmd.tmpl deleted file mode 100644 index 10c4e95..0000000 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/cmd.tmpl +++ /dev/null @@ -1 +0,0 @@ -enapter3 device change-blueprint {{.BaseFlags}} --device-id 3b0a0626-2dc4-44a3-ac5a-34d58b7b2a26 --blueprint-path ./testdata/blueprints/simple diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/cmd.tmpl deleted file mode 100644 index abb0189..0000000 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/cmd.tmpl +++ /dev/null @@ -1 +0,0 @@ -enapter3 device change-blueprint {{.BaseFlags}} --device-id 3b0a0626-2dc4-44a3-ac5a-34d58b7b2a26 --blueprint-path ./testdata/blueprints/bp.zip diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/cmd.tmpl deleted file mode 100644 index c658f5c..0000000 --- a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/cmd.tmpl +++ /dev/null @@ -1 +0,0 @@ -enapter3 device change-blueprint {{.BaseFlags}} --device-id 427ec09e-ec1e-4760-acc1-50106533b875 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id/cmd.tmpl new file mode 100644 index 0000000..dc26dc1 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device change-blueprint --connection my-conn --device-id 427ec09e-ec1e-4760-acc1-50106533b875 --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/out b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id/out similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/out rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id/out diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id/req_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/req_0 rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id/req_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id/resp_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id/resp_0 rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id/resp_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id_and_path/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id_and_path/cmd.tmpl new file mode 100644 index 0000000..17c9b3a --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id_and_path/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device change-blueprint --connection my-conn --device-id 427ec09e-ec1e-4760-acc1-50106533b875 --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 --blueprint-path ./testdata/blueprints/bp.zip diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/out b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id_and_path/out similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_id_and_path/out rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_id_and_path/out diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/cmd.tmpl new file mode 100644 index 0000000..1769b48 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device change-blueprint --connection my-conn --device-id 3b0a0626-2dc4-44a3-ac5a-34d58b7b2a26 --blueprint-path ./testdata/blueprints/simple diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/out b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/out similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/out rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/out diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/req_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_0 rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/req_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_1 b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/req_1 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/req_1 rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/req_1 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/resp_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/resp_0 rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/resp_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/resp_1 b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/resp_1 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_path/resp_1 rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_path/resp_1 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/cmd.tmpl new file mode 100644 index 0000000..0097e3a --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device change-blueprint --connection my-conn --device-id 3b0a0626-2dc4-44a3-ac5a-34d58b7b2a26 --blueprint-path ./testdata/blueprints/bp.zip diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/out b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/out similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/out rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/out diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/req_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_0 rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/req_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_1 b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/req_1 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/req_1 rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/req_1 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/resp_0 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/resp_0 rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/resp_0 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/resp_1 b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/resp_1 similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_by_zip/resp_1 rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_by_zip/resp_1 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_without_blueprint/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_without_blueprint/cmd.tmpl new file mode 100644 index 0000000..18c0433 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_without_blueprint/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device change-blueprint --connection my-conn --device-id 427ec09e-ec1e-4760-acc1-50106533b875 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/out b/internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_without_blueprint/out similarity index 100% rename from internal/app/enaptercli/testdata/http_req_resp/device_assign_blueprint_without_blueprint/out rename to internal/app/enaptercli/testdata/http_req_resp/device_change_blueprint_without_blueprint/out diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_blueprint_id_and_path/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_blueprint_id_and_path/cmd.tmpl new file mode 100644 index 0000000..555e0c4 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_blueprint_id_and_path/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device create lua-device --connection my-conn --runtime-id my-runtime --device-name my-device --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 --blueprint-path ./testdata/blueprints/bp.zip diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_blueprint_id_and_path/out b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_blueprint_id_and_path/out new file mode 100644 index 0000000..dc69131 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_blueprint_id_and_path/out @@ -0,0 +1 @@ +app exit with error: only one of --blueprint-id or --blueprint-path can be specified diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/cmd.tmpl new file mode 100644 index 0000000..d291224 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} --site-id my-site +enapter3 device create lua-device --connection my-conn --runtime-id my-runtime --device-name my-device --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/out b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/out new file mode 100644 index 0000000..dc63955 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/out @@ -0,0 +1 @@ +{"device":{"id":"1b6adca2-3a9f-4bfe-95b4-92b28a581055"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/req_0 new file mode 100644 index 0000000..34f782b --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/sites/my-site/devices/my-runtime", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/req_1 b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/req_1 new file mode 100644 index 0000000..9a91017 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/req_1 @@ -0,0 +1,22 @@ +{ + "Method": "POST", + "URL": "/v3/provisioning/lua_device", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Length": [ + "136" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "{\"blueprint_id\":\"cdd82438-dda8-4f69-aad1-0be9adeab964\",\"name\":\"my-device\",\"runtime_id\":\"427ec09e-ec1e-4760-acc1-50106533b875\",\"slug\":\"\"}" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/resp_0 new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/resp_0 @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/resp_1 b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/resp_1 new file mode 100644 index 0000000..dc63955 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_with_site_id/resp_1 @@ -0,0 +1 @@ +{"device":{"id":"1b6adca2-3a9f-4bfe-95b4-92b28a581055"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_blueprint/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_blueprint/cmd.tmpl new file mode 100644 index 0000000..fa50e46 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_blueprint/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device create lua-device --connection my-conn --runtime-id my-runtime --device-name my-device diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_blueprint/out b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_blueprint/out new file mode 100644 index 0000000..8ccadaf --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_blueprint/out @@ -0,0 +1 @@ +app exit with error: one of --blueprint-id or --blueprint-path must be specified diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/cmd.tmpl new file mode 100644 index 0000000..a8bd9e8 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device create lua-device --connection my-conn --runtime-id my-runtime --device-name my-device --blueprint-id cdd82438-dda8-4f69-aad1-0be9adeab964 diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/out b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/out new file mode 100644 index 0000000..dc63955 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/out @@ -0,0 +1 @@ +{"device":{"id":"1b6adca2-3a9f-4bfe-95b4-92b28a581055"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/req_0 new file mode 100644 index 0000000..569c3ba --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/req_0 @@ -0,0 +1,22 @@ +{ + "Method": "POST", + "URL": "/v3/provisioning/lua_device", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Length": [ + "110" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "{\"blueprint_id\":\"cdd82438-dda8-4f69-aad1-0be9adeab964\",\"name\":\"my-device\",\"runtime_id\":\"my-runtime\",\"slug\":\"\"}" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/resp_0 new file mode 100644 index 0000000..dc63955 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_lua_without_site_id/resp_0 @@ -0,0 +1 @@ +{"device":{"id":"1b6adca2-3a9f-4bfe-95b4-92b28a581055"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_standalone_without_site_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_create_standalone_without_site_id/cmd.tmpl new file mode 100644 index 0000000..88631d2 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_standalone_without_site_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device create standalone --connection my-conn --device-name my-device diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_create_standalone_without_site_id/out b/internal/app/enaptercli/testdata/http_req_resp/device_create_standalone_without_site_id/out new file mode 100644 index 0000000..9789fa3 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_create_standalone_without_site_id/out @@ -0,0 +1 @@ +app exit with error: site ID is required diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/cmd.tmpl new file mode 100644 index 0000000..ac61885 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device get --connection my-conn --site-id my-site --device-id my-device diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/out b/internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/out new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/out @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/req_0 new file mode 100644 index 0000000..99cb7dd --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/sites/my-site/devices/my-device", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/resp_0 new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_get_with_site_id/resp_0 @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/cmd.tmpl new file mode 100644 index 0000000..713bef9 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 device get --connection my-conn --device-id my-device diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/out b/internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/out new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/out @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/req_0 new file mode 100644 index 0000000..e16fe8e --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/devices/my-device", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/resp_0 new file mode 100644 index 0000000..9e7cb4d --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/device_get_without_site_id/resp_0 @@ -0,0 +1 @@ +{"device":{"id":"427ec09e-ec1e-4760-acc1-50106533b875"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/cmd.tmpl new file mode 100644 index 0000000..0988d20 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 rule-engine get --connection my-conn --site-id my-site diff --git a/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/out b/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/out new file mode 100644 index 0000000..3fbd0b3 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/out @@ -0,0 +1 @@ +{"engine":{"id":"7cbdf086-4555-428d-9264-29c3052d71dd"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/req_0 new file mode 100644 index 0000000..b6b1edd --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/sites/my-site/rule_engine", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/resp_0 new file mode 100644 index 0000000..3fbd0b3 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_with_site_id/resp_0 @@ -0,0 +1 @@ +{"engine":{"id":"7cbdf086-4555-428d-9264-29c3052d71dd"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_without_site_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_without_site_id/cmd.tmpl new file mode 100644 index 0000000..7e61b91 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_without_site_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 rule-engine get --connection my-conn diff --git a/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_without_site_id/out b/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_without_site_id/out new file mode 100644 index 0000000..9789fa3 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/rule_engine_get_without_site_id/out @@ -0,0 +1 @@ +app exit with error: site ID is required diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/cmd.tmpl new file mode 100644 index 0000000..fff957c --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 site get --connection my-conn --site-id my-site diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/out b/internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/out new file mode 100644 index 0000000..86a96ed --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/out @@ -0,0 +1 @@ +{"site":{"id":"7cbdf086-4555-428d-9264-29c3052d71dd"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/req_0 new file mode 100644 index 0000000..619b9c4 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/sites/my-site", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/resp_0 new file mode 100644 index 0000000..86a96ed --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_get_with_site_id/resp_0 @@ -0,0 +1 @@ +{"site":{"id":"7cbdf086-4555-428d-9264-29c3052d71dd"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_get_without_site_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/site_get_without_site_id/cmd.tmpl new file mode 100644 index 0000000..7ff9adf --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_get_without_site_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 site get --connection my-conn diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_get_without_site_id/out b/internal/app/enaptercli/testdata/http_req_resp/site_get_without_site_id/out new file mode 100644 index 0000000..9789fa3 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_get_without_site_id/out @@ -0,0 +1 @@ +app exit with error: site ID is required diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/cmd.tmpl new file mode 100644 index 0000000..b0fdd24 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} --site-id my-site +enapter3 site list --connection my-conn diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/out b/internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/out new file mode 100644 index 0000000..87fda06 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/out @@ -0,0 +1,2 @@ +WARNING: trying to get sites list when site ID is set for current connection, result will contain only one site. +{"sites":[{"id":"7cbdf086-4555-428d-9264-29c3052d71dd"}],"total_count":1} diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/req_0 new file mode 100644 index 0000000..619b9c4 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/sites/my-site", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/resp_0 new file mode 100644 index 0000000..86a96ed --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_list_with_site_id/resp_0 @@ -0,0 +1 @@ +{"site":{"id":"7cbdf086-4555-428d-9264-29c3052d71dd"}} diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/cmd.tmpl b/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/cmd.tmpl new file mode 100644 index 0000000..62a108c --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/cmd.tmpl @@ -0,0 +1,2 @@ +enapter3 connection add --name my-conn --url {{.URL}} --token {{.Token}} +enapter3 site list --connection my-conn diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/out b/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/out new file mode 100644 index 0000000..402bf28 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/out @@ -0,0 +1 @@ +{"sites":[{"id":"7cbdf086-4555-428d-9264-29c3052d71dd"}],"total_count":1} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/req_0 b/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/req_0 new file mode 100644 index 0000000..07fca04 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/req_0 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/sites?limit=50\u0026offset=0", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/req_1 b/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/req_1 new file mode 100644 index 0000000..f10ffb3 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/req_1 @@ -0,0 +1,19 @@ +{ + "Method": "GET", + "URL": "/v3/sites?limit=50\u0026offset=1", + "Header": { + "Accept-Encoding": [ + "gzip" + ], + "Content-Type": [ + "" + ], + "User-Agent": [ + "enapter-cli/" + ], + "X-Enapter-Auth-Token": [ + "enapter_api_test_token" + ] + }, + "Body": "" +} \ No newline at end of file diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/resp_0 b/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/resp_0 new file mode 100644 index 0000000..5b3b589 --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/resp_0 @@ -0,0 +1 @@ +{"sites":[{"id":"7cbdf086-4555-428d-9264-29c3052d71dd"}],"total_count":1} diff --git a/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/resp_1 b/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/resp_1 new file mode 100644 index 0000000..d27230f --- /dev/null +++ b/internal/app/enaptercli/testdata/http_req_resp/site_list_without_site_id/resp_1 @@ -0,0 +1 @@ +{"sites":[],"total_count":1}